1. 程式人生 > >Spring Boot【原理分析】(3)——BeanDefinition

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,進行統一呼叫。