Spring Boot【原理分析】(3)——BeanDefinition
一、簡介
BeanDefinition描述了一個Bean的例項,包括屬性,構造方法引數,註解等更多資訊。為後面例項化Bean提供元資料依據。
BeanDefinition的實現類有:
1. RootBeanDefinition:spring BeanFactory執行期裡,內部特殊bean的定義。
2. ConfigurationClassBeanDefinition:繼承RootBeanDefinition,ConfigurationClassBeanDefinitionReader內部靜態類。上文(2)中提到的Configuration Class內定義的Bean被解析成這個BeanDefinition。
3. ChildBeanDefinition:Spring2.5後棄用。
4. GenericBeanDefinition:@EnableConfigurationProperties和Spring顯示建立。@ImportResource
5. ScannedGenericBeanDefinition:上文(2)中提到的ComponentScanAnnotationParser掃描的@Component定義被解析成這個BeanDefinition。
6. AnnotatedGenericBeanDefinition:Spring Boot autoconfigure功能spring.facotries中定義的EnableAutoConfiguration,以及內部子Configuration。@Import
二、建立過程
1.GenericBeanDefinition:
這裡主要看一下@ImportResource xml中定義的bean。
ConfigurationClassBeanDefinitionReader解析Configuration Class時會解析ImportResource,XmlBeanDefinitionReader具體解析xml中的Beans,最終由BeanDefinitionParserDelegate解析每個Bean:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this .parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//構建GenericBeanDefinition並設定className和parentName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//對scope、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method、factory-bean屬性進行解析。
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析meta資料,用於注入。<meta key="format" value="BLURAY"/>
parseMetaElements(ele, bd);
//解析lookup-method資料,用於abstract方法動態生產bean。<lookup-method name="createCommand" bean="myCommand"/>
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
/**
* 解析replaced-method資料,用於根據引數型別替換方法。
* <replaced-method name="computeValue" replacer="replacementComputeValue">
* <arg-type>String</arg-type>
* </replaced-method>
**/
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析constructor-arg,建構函式引數。可以index,type,name匹配。
parseConstructorArgElements(ele, bd);
// 解析property資料,用於屬性賦值或注入。<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
parsePropertyElements(ele, bd);
/**
* 解析qualifier資料,用於注入,和meta類似。
* <qualifier type="MovieQualifier">
* <attribute key="format" value="VHS"/>
* <attribute key="genre" value="Action"/>
* </qualifier>
**/
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
解析完基本BeanDefinition後,需要呼叫decorateBeanDefinitionIfRequired
對NameSpace屬性進行解析如p:name=”test”。
到此定義在xml中Bean解析成GenericBeanDefinition完成。
2.ScannedGenericBeanDefinition
ScannedGenericBeanDefinition通過掃描Component,再通過MetadataReader獲取AnnotationMetadata構建。重點關注MetadataReader的獲取。
MetadataReader直接通過建構函式構建:
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader;
try {
classReader = new ClassReader(is);
}
catch (IllegalArgumentException ex) {
throw new NestedIOException("ASM ClassReader failed to parse class file - " +
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
}
finally {
is.close();
}
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
this.classMetadata = visitor;
this.resource = resource;
}
從原始碼可以看出, annotationMetadata 直接直接通過讀取java class檔案獲取資訊,後面bean的建立,就使用了這個annotationMetadata 。
3.ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition是定義在Configuration Class中的Bean,在這個bean建立是需要呼叫到Configuration Class中定義bean的方法。所以這個BeanDefinition的重點(特殊點)在於factoryMethodMetadata,即Configuration Class的對應的MethodMetaData。再通過解析Bean註解和Scope註解設定相應的值。
4.AnnotatedGenericBeanDefinition
@Import中定義的Bean,是作為Configuration Class來進行解析的,在ConfigurationClassParser
時已經解析成一個Configuration Class,並生成了metadata。後續和Component生成ScannedGenericBeanDefinition類似。spring.facotries中的EnableAutoConfiguration也是在ConfigurationClassParser的時候掃描得到Configuration Class。
5.ConfigurationClass
上面幾個BeanDefinition建立是需要用的ConfigurationClass, ConfigurationClass是何時建立的,怎麼建立的?
ConfigurationClassParser中建立ConfigurationClass原始碼:
protected final void parse(String className, String beanName) throws IOException {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
public ConfigurationClass asConfigClass(ConfigurationClass importedBy) throws IOException {
if (this.source instanceof Class<?>) {
return new ConfigurationClass((Class<?>) this.source, importedBy);
}
return new ConfigurationClass((MetadataReader) this.source, importedBy);
}
第一個是在遞迴解析Component時建立。最後一個是在解析內部成員類和Import類。
6.getMergedBeanDefinition
在構建Bean例項是,都會獲取MergedBeanDefinition,這個功能是把所有不是RootBeanDefinition例項的轉成RootBeanDefinition,進行統一呼叫。