1. 程式人生 > >Spring原始碼系列 — 註解原理

Spring原始碼系列 — 註解原理

前言

前文中主要介紹了Spring中處理BeanDefinition的擴充套件點,其中著重介紹BeanDefinitionParser方式的擴充套件。本篇文章承接該內容,詳解Spring中如何利用BeanDefinitionParser的特性實現註解配置的解析。本文主要從以下幾個方面介紹Spring中的註解配置解析原理:

  • @Component系註解配置的作用原理
  • @Autowired註解配置的作用原理

無論註解配置還是XML配置,只是外在配置形式的變化,但是Spring的核心仍然是相同的:

@Component系註解配置的作用原理

在介紹@Component原理前,先總結下@Component系的註解:

  • @Service
  • @Repository
  • @Configuration

以上這些都均屬於@Component系註解,均被Component修飾。其實Spring應用者需要明白無論是XML配置,還是以上的這些註解配置,它們只是配置形式上的變化,但是內在表述這種配置的BeanDefinition模型仍然是統一的。形式上的變化,只會導致適配該形式的解析會發生變化。下文將從原始碼的角度先分析Component系註解配置的解析。

涉及到Bean配置的解析自然不能脫離前文中講述到的BeanDefinitionParser(如果還沒有閱讀過前文的讀者,這裡送一個傳送門Spring原始碼系列 — BeanDefinition擴充套件點

)。

同時大家也應該知道觸發@Component註解生效,需要配置XML:<component-scan/>。

從前文的ContextNameSpaceHandler中可以看到component-scan該配置由ComponentScanBeanDefinitionParser負責解析。該BeanDefinitionPaser是開始處理@Component的入口,負責將被@Component系註解修飾的配置解析為BeanDefinition。

同樣ComponentScanBeanDefinitionParser實現了parse方法:

// element為XML配置元素,parserContext解析上下文
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 獲取該元素的base-package的屬性
    // spring應用者,應該不陌生<context:component-scan base-package="..."/>這樣的配置
    // 獲取被@Componet系註解配置的類所在的包路徑
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    // 利用環境元件解析路徑,處理其中的佔位。關於這部分內容在前文中已經介紹
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // 因為base-package可以配置多個,所以這裡根據分隔符進行分割處理,形成包路徑陣列。預設分割符為:",; \t\n"
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    // Actually scan for bean definitions and register them.
    // 建立Bean定義的掃描器元件
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 掃描basePackages下被@Componet系註解修飾的Bean,形成BeanDefinition
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // 註冊公共的元件
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

以上的邏輯已經非常清晰明瞭,對於使用環境元件處理basePackages中的佔位部分,如果讀者不瞭解,這裡有傳送門Spring原始碼系列 — Envoriment元件。 繼續學習configureScanner中如果建立Scanner。主要分為兩部分:

  • 建立Scanner
  • 配置Scanner
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    // 解析componet-scan中配置的use-default-filters屬性
    boolean useDefaultFilters = true;
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
        useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }
    // 建立Bean定義Scanner
    // Delegate bean definition registration to scanner class.
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    // 配置預設的BeanDefinitionDefaults,該物件持有預設的Bean定義屬性
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    // 配置自動注入候選者模式
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
    // 配置資源模式
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
        scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }
    try {
        // 配置BeanName生成器
        parseBeanNameGenerator(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
    try {
        // 配置作用域
        parseScope(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
    // 配置型別過濾器
    parseTypeFilters(element, scanner, parserContext);
    return scanner;
}

關於以上配置Scanner由幾個地方需要重點關注:

  1. 預設的過濾器
  2. 配置預設的BeanDefinitionDefaults
  3. 配置BeanName生成器
  4. 配置型別過濾器

BeanDefinitionDefaults是持有BeanDefinition屬性的預設配置,其中有是否惰性初始化、自動裝配模式、初始化/銷燬方法等。這些Bean定義屬性將會作為預設值配置到將要被解析的BeanDefinition中。
BeanName生成器將會為沒有配置Bean名字的BeanDefinition生成Bean名字。
型別過濾器非常重要,使用base-packages配置了包路徑,這些路徑下的類需要被過濾。如使用@Component註解修飾的類將會被過濾,然後解析為BeanDefinition,其他的類將不會被處理。
其中有變數useDefaultFilters標誌是否使用預設的過濾器,我們繼續看下createScanner的實現:

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    // 直接new創鍵Scanner
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
            readerContext.getEnvironment(), readerContext.getResourceLoader());
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment, ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 賦值BeanDefinitionRegistry
    this.registry = registry;
    // 判斷是否使用預設的過濾器
    if (useDefaultFilters) {
        // 註冊預設的過濾器
        registerDefaultFilters();
    }
    // 設定環境元件
    setEnvironment(environment);
    // 設定資源載入器
    setResourceLoader(resourceLoader);
}

以上需要注意的是resourceLoader,前文在介紹BeanDefinition解析時,提到XmlReaderContext是用於持有Spring解析XML配置時的各種元件資訊,其中包括資源載入器。這裡將資源載入器傳給Scanner,Scanner通過組合ResourceLoader從而具有了載入資源的能力。

同時需要注意useDefaultFilters,它用於控制是否註冊預設的型別過濾器,繼續看下預設的過濾器是什麼:

protected void registerDefaultFilters() {
    // includeFilters中新增關於Component註解的過濾器
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 新增ManagedBean註解的過濾器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        // 新增Named註解的過濾器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

從以上預設的過濾器可以看出,被@Component、@ManagedBean、@Named註解修飾的類都將被註冊為Bean。

建立和配置Scanner的邏輯雖然不復雜,當時呼叫鏈路還是有點繞彎。這裡整理下:

Scanner主要完成兩項邏輯:

  1. 負責掃描目標包路徑下的註解配置,並將其解析為BeanDefinition;
  2. 將解析出的BeanDefinition註冊到BeanFactory中;

其中為了實現包路徑下的Bean配置的擴充套件性、動態配置解析,Scanner中通過組合兩種型別過濾器:

  1. 需要解析的過濾器
  2. 排除解析的過濾器

該兩種過濾器可以在

// 包含的過濾器
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();

// 排除的過濾器
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();

在瞭解Scanner的建立和配置後,再來看其如何載入資源,檢測並解析BeanDefinition和註冊。

其中doScan中完成以上的三步邏輯:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 建立將被解析的BeanDefinition容器
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    // 迴圈遍歷掃描多個包路徑
    for (String basePackage : basePackages) {
        // 在basePackage下尋找BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 迴圈遍歷已尋找到的BeanDefinition
        for (BeanDefinition candidate : candidates) {
            // 解析該candidate的作用域
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成BeanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 如果是AbstractBeanDefinition,則後置處理BeanDefinition
            // 主要將BeanDefinitionDefaults的屬性覆蓋到BeanDefiniton中
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 處理該BeanDefinition的一些公共的註解資訊
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 檢查該Bean定義是否已經存在,或者與已存在的Bean定義是否相容
            if (checkCandidate(beanName, candidate)) {
                // 註冊Bean定義至BeanFactory中
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

首先看前兩步驟,載入資源然後檢測解析Bean定義,這個過程是由findCandidateComponents完成:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    // 建立Bean定義容器
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
        // 基於給定的包路徑組裝成檢索的路徑:
        // 1. 將包路徑分隔符替換成檔案系統分隔符
        // 2. 加上資源解析模式字首"classpath*:"
        // 3. 加上資源模式字尾"**/*.class",用於表示路徑層次和要檢測的檔案型別為.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 使用resourceloader獲取給定的檢索路徑上的所有匹配的資源
        // 關於這部分邏輯在介紹BeanDefinition一文中已經詳細介紹,這裡不再贅述
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // 遍歷每個資源
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            // 如果資源是可讀
            if (resource.isReadable()) {
                try {
                    // 這裡使用工廠模式獲取該資源的元資料閱讀器(MetadataReader)
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    // 根據MetadataReader判斷是否為匹配的候選者Component
                    if (isCandidateComponent(metadataReader)) {
                        // 如果是,則構造ScannedGenericBeanDefinition型別的Bean定義
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        // 設定Bean定義的resourc和source屬性
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // 決策該class是否為滿足條件的候選者
                        // 1. 需要滿足是非抽象類;2. 需要滿足是頂級的無依賴類(即非巢狀的內部類)
                        if (isCandidateComponent(sbd)) {
                            // 如果是候選者,則加入bean定義容器中
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            // 資源不可讀,則忽略跳過該資源
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

在細說這段邏輯之前,先來了解一些基礎的元件:MetadataReader、MetadataReaderFactory、ClassMetadata、AnnotationMetadata、ScannedGenericBeanDefinition、TypeFilter。

MetadataReader是Spring封裝用於訪問class檔案的門面工具,通過它可以獲取到class的元資訊。主要是通過ASM庫的ClassReader實現。從介面定義上分析:

public interface MetadataReader {
    // 獲取class類的元資訊
    ClassMetadata getClassMetadata();
    // 獲取該類被修飾的註解的元資訊
    AnnotationMetadata getAnnotationMetadata();
}

MetadataReaderFactory也是Spring封裝用於建立指定類的MetadataReader門面。即這裡使用了工廠模式。通過其介面抽象可以可以體會到其工廠的設計理念:

public interface MetadataReaderFactory {
    // 根據類名獲取指定的MetadataReader
    MetadataReader getMetadataReader(String className) throws IOException;
    // 根據指定的resource獲取對應的MetadataReader
    MetadataReader getMetadataReader(Resource resource) throws IOException;
}

ClassMetadata是該類的元資訊的抽象,其中提供大量的介面用於描述該類的特徵。如:

// 獲取該類類名
String getClassName();
// 是否為介面
boolean isInterface();
boolean isAnnotation();
boolean isAbstract();
// 是否為實現類
boolean isConcrete();
// 是否是final
boolean isFinal();
boolean isIndependent();
boolean hasEnclosingClass();
String getEnclosingClassName();

AnnotationMetadata是修飾該類的註解資訊的抽象,它用於描述修飾該類註解的元資訊。包含了修飾的註解所有資訊,包括註解本身的屬性。

// 獲取所有的註解型別
Set<String> getAnnotationTypes();
// 根據名稱獲取註解屬性
Map<String, Object> getAnnotationAttributes(String annotationName);

Spring通過提供這套元件,可以非常便捷的訪問類資訊。它底層的原理是依賴ASM位元組碼庫實現。下圖描述了大致原理:

Tips:
Spring中封裝的MetadataReader和MetadataReaderFactory,筆者認為本身定位也是作為工具使用,所以日常應用中開發中,完全可以使用其提供的能力。當然是Spring應用才是最好這樣使用。

Note:
從以上的介紹中,不可忽略的是這裡訪問class資訊,並未涉及到類載入器系統將該類載入至JVM中形成class物件。這個需要重點關注:該類尚未被載入。只是被ASM載入了而已!

同時可以通過以下的例子更深入的瞭解其特性和API:

String className = AnnotatedTargetClass.class.getName();
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println("isConcrete:" + classMetadata.isConcrete());
System.out.println("isAbstract:" + classMetadata.isAbstract());
System.out.println("isFinal:" + classMetadata.isFinal());
System.out.println("isIndependent:" + classMetadata.isIndependent());
System.out.println("isInterface:" + classMetadata.isInterface());
System.out.println("isAnnotation:" + classMetadata.isAnnotation());

AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
System.out.println("all annotations type:" + String.join(",",
        annotationMetadata.getAnnotationTypes()));
Map<String, Object>  annotationAttrs = annotationMetadata
        .getAnnotationAttributes(XmlRootElement.class.getName());
Set<Map.Entry<String, Object>> entries = annotationAttrs.entrySet();
for (Map.Entry<String, Object> entry : entries) {
    System.out.println("attribute name:" + entry.getKey() + ", attribute name:" + entry.getValue());
}

在介紹完訪問類的元資訊後,繼續看下ScannedGenericBeanDefinition和TypeFilter。

首先ScannedGenericBeanDefinition是一種BeanDefinition型別,它是擴充套件GenericBeanDefinition實現,同時基於ASM增加了對AnnotatedBeanDefinition註解暴露介面的支援,即實現了AnnotatedBeanDefinition介面。提供獲取AnnotationMetadata和MethodMetadata的資訊。通過註解形式配置的Bean定義需要轉化為該型別的BeanDefinition。因為它可以提供獲取註解資訊。

TypeFilter是型別過濾器,通過抽象介面:

boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
        throws IOException;

匹配過濾指定的metadataReader。metadataReader本身代表class的訪問器,通過TypeFilter篩選出需要的metadataReader,然後將其轉化為BeanDefinition。

在瞭解一些基礎元件後,便可以深入分析Spring如何篩選出被@Componet系註解修飾的類。

上述程式碼在遍歷每個Resource中,利用isCandidateComponent(metadataReader)篩選指定的metadataReader:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    // 首先根據排除過濾器進行匹配,然後排除掉metadataReader
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return false;
        }
    }
    // 然後根據包含過濾器篩選出滿足的metadataReader
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

前文在介紹excludeFilters和includeFilters正是被這裡使用。在預設情況下(componet-scan中未做任何filter的配置時),只有includeFilters中包含@Component的AnnotationTypeFilter,這個由前文中介紹的registerDefaultFilters將其註冊。

由於篇幅原因,這裡不在詳述match方法的實現。但是原理就是:

  1. 首先匹配自身類的metadataReader,即將metadataReader中的註解與AnnotationTypeFilter中的註解進行比較,如果metadataReader中的註解包含了AnnotationTypeFilter的,則認為是匹配;
  2. 否則再獲取metadataReader的父類的metadataReader,再如上的方式比較;

可以看下其比較方式:

// 獲取所有的註解元資訊
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// 如果註解中包含AnnotationTypeFilter指定的註解
return metadata.hasAnnotation(this.annotationType.getName()) ||
        (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));

通過以上的方式可以成功的找到@Component系註解的所有metadataReader,然後利用其構造ScannedGenericBeanDefinition。然後再看下如何篩選BeanDefinition的isCandidateComponent:

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    // 獲取BeanDefintion中的註解元資訊
    AnnotationMetadata metadata = beanDefinition.getMetadata();
    // 抉擇該類是否為實現和無依賴類,或者該類是抽象類但是有Lookup註解修飾的方法
    return (metadata.isIndependent() && (metadata.isConcrete() ||
            (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

到這裡,關於資源的載入、@Component系註解配置檢測、解析為BeanDefition的過程基本完成,這裡再總結下:

  1. 迴圈遍歷所有的包路徑
  2. 將每個包路徑組轉成滿足解析器的搜尋路徑格式
  3. 根據搜尋路徑載入所有資源
  4. 遍歷所有資源,將資源轉化成MetadataReader
  5. 根據MetadataReader和TypeFilter進行匹配,篩選出符合的MetadataReader
  6. 根據MetadataReader建立ScannedGenericBeanDefinition

再繼續生成BeanName的邏輯:

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    // 如果是註解型BeanDefinition
    if (definition instanceof AnnotatedBeanDefinition) {
        // 從註解配置中解析BeanName
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
        // 如果註解中指定了BeanName,則返回註解中配置的BeanName
        if (StringUtils.hasText(beanName)) {
            // Explicit bean name found.
            return beanName;
        }
    }
    // 如果註解中沒有配置,則根據Bean的ShortClassName(即簡單的class名字,非全限定名)生成
    // 生成時,className的首字母小寫
    // Fallback: generate a unique default bean name.
    return buildDefaultBeanName(definition, registry);
}

然後再繼續看處理公共註解的邏輯AnnotationConfigUtils.processCommonDefinitionAnnotations

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    // 處理公共註解
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    // 處理惰性初始化註解,將BeanDefinition設定成Lazy
    if (metadata.isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
    }

    else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
    }
    // 處理Primary註解,將BeanDefinition設定為首選的主要候選者
    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    }
    // 解析DependsOn註解
    if (metadata.isAnnotated(DependsOn.class.getName())) {
        abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
    }
    // 處理Role和Description註解
    if (abd instanceof AbstractBeanDefinition) {
        AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
        if (metadata.isAnnotated(Role.class.getName())) {
            absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
        }
        if (metadata.isAnnotated(Description.class.getName())) {
            absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
        }
    }
}

以上公共的註解處理邏輯中關於惰性初始化的尤其需要注意。

對BeanDefinition解析完成後,接下來就是將其註冊到BeanFactory中。但是在註冊之前需要進行檢查該BeanDefinition是否已經存在,防止重複註冊。註冊BeanDefinition的邏輯相信大家應該不陌生了,就是使用BeanDefinition註冊器將BeanDefinition註冊至BeanFactory。

完成了BeanDefinition的註冊,關於@Component系註解的大部分工作就已經完成了。但是還剩下最後的註冊公共的元件步驟,這些公共元件主要用處理特定的註解:

  1. 註冊處理@Configuration註解的處理器ConfigurationClassPostProcessor,其中有包含處理@Import,@ Configuration,@Bean等邏輯;
  2. 註冊處理@Autowired和Value註解的處理器AutowiredAnnotationBeanPostProcessor,其中主要處理Bean的註解式自動裝配邏輯;
  3. 註冊處理@Required註解的處理器RequiredAnnotationBeanPostProcessor;
  4. 註冊處理@PreDestroy,@PostConstruct,@Resource(JSR-250的註解)這些java自身的註解處理器CommonAnnotationBeanPostProcessor
protected void registerComponents(
        XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
    // 定義複合元件定義物件
    Object source = readerContext.extractSource(element);
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
    // 將掃描出的BeanDefinition新增到符合元件定義中
    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }
    // 從xml中獲取是否註冊公共註解處理器的配置屬性
    // Register annotation config processors, if necessary.
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
     // 根據配置判斷是否註冊一些公共的處理註解配置處理器
    if (annotationConfig) {
        // 註冊公共的註解處理器
        Set<BeanDefinitionHolder> processorDefinitions =
                AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        // 將公共的註解處理器加入符合元件定義中
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
        }
    }
    // 發起元件註冊事件
    readerContext.fireComponentRegistered(compositeDef);
}

以上主要是獲取配置,判斷是否註冊公共的註解配置處理器,其中註冊的邏輯由AnnotationConfigUtils.registerAnnotationConfigProcessors工具方法實現。

其中關於ConfigurationClassPostProcessor的註解處理器還是非常重要,但是由於篇幅原因這裡不再詳述。後續可能會不上一篇單獨介紹他。

到這裡,關於@Component系註解的作用原理就已經介紹完成,後續就是Bean的例項化的邏輯,關於該內容請參考之前的文章,這裡不再贅述。

接下來就開始介紹以上註解處理器中的另一個至關重要的角色:AutowiredAnnotationBeanPostProcessor

@Autowired註解配置的作用原理

關於@Autowired的作用相信讀者應該都知道:即註解式自動裝配。類似XML配置一樣,配置物件之間的依賴關係,使用也是非常簡單。但是關於該註解的內部實現原理,很多使用者都知之甚少,本節將對其原理進行深入介紹。

先分析@Autowired註解完成的工作,以及這些工作應該在Spring的哪些階段中完成相應的邏輯。

@Autowired作用在方法或者成員域上,將依賴的Bean注入。比如A依賴B,A類中有B類性的成員,在B成員域上加上@Autowired,Spring將會管理這種依賴,將B的例項注入A的例項中(前提A和B都需要被註冊為Bean)。這個工作需要完成兩件事:

  • 解析@Autowired配置
  • 尋找依賴的Bean,將依賴的Bean注入

其中解析@Autowired配置屬於解析BeanDefinition階段,尋找依賴的Bean,將依賴的Bean注入屬於在裝配階段。Spring中在解析@Autowired配置,雖然是解析BeanDefinition,但是是在例項化階段之後解析,並非在解析BeanDefinition階段。

關於以上兩部分邏輯由Spring中的AutowiredAnnotationBeanPostProcessor處理器負責,在上節中已經介紹在處理

首先看下其javadocs的部分描述:

{@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
that autowires annotated fields, setter methods and arbitrary config methods.
Such members to be injected are detected through a Java 5 annotation: by default,
Spring's {@link Autowired @Autowired} and {@link Value @Value} annotations. Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation, if available, as a direct alternative to Spring's own {@code @Autowired}.

從docs中可以看出,其是BeanPostProcessor的實現,用於處理在域和setter方法上的@ Autowired註解和@Value註解。同時還支援jsr-330的註解@Inject。

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {}

該類通過同時還實現了MergedBeanDefinitionPostProcessor介面,該介面是對BeanPostProcessor的更進一步抽象,提供對合並BeanDeifnition的回撥處理。其實現該介面,實現對合並BeanDeifnition解析,最終完成對@Autowired和@Value的解析。

通過繼承實現InstantiationAwareBeanPostProcessorAdapter抽象類,以實現InstantiationAwareBeanPostProcessor介面,從而完成對該方法postProcessPropertyValues的實現,在這裡完成對依賴的Bean的尋找和注入裝配工作。InstantiationAwareBeanPostProcessor也是對BeanPostProcessor的更進一步抽象,提供對Bean例項化之前和之後的後置處理回撥,也提供了對裝配階段的後置處理回撥postProcessPropertyValues。

MergedBeanDefinitionPostProcessor在Bean例項化之後,被觸發。InstantiationAwareBeanPostProcessor對裝配階段的後置處理回撥,是在裝配之前觸發,對於詳情可以參考我的另一篇文章Spring原始碼系列 — Bean生命週期

首先看AutowiredAnnotationBeanPostProcessor的結構

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

    // 自動注入的註解型別,能夠處理的自動注入註解的型別都存在該集合中
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes =
            new LinkedHashSet<Class<? extends Annotation>>(4);

    // 硬編碼,註解引數required
    private String requiredParameterName = "required";

    private boolean requiredParameterValue = true;

    // 實現beanFactoryAware介面,准入BeanFactory
    private ConfigurableListableBeanFactory beanFactory;

    // 自動注入元資訊集合,儲存被自動注入註解修飾的資訊
    private final Map<String, InjectionMetadata> injectionMetadataCache =
            new ConcurrentHashMap<String, InjectionMetadata>(256);
}

其中autowiredAnnotationTypes集合儲存能夠被處理的註解資訊:

public AutowiredAnnotationBeanPostProcessor() {
    // 增加對@Autowired註解的支援
    this.autowiredAnnotationTypes.add(Autowired.class);
    // 增加對@Vaule的註解的支援
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        // 增加對Inject註解的支援
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

然後便是解析@Autowired和@Value配置,將其抽象成InjectionMetadata物件,存入其集合中。InjectionMetadata結構如下:

public class InjectionMetadata {
    // 目標型別
    private final Class<?> targetClass;
    // 被注入元素的集合
    private final Collection<InjectedElement> injectedElements;
    // 被檢查元素的集合
    private volatile Set<InjectedElement> checkedElements;
}

該例項中抽象有InjectedElement,它是用來描述單個被注入的資訊,如:

@Autowired
private A a;

將被解析成InjectedElement用於描述:

public abstract static class InjectedElement {
    // 被註解的成員,Field還是Method
    protected final Member member;
    // 是否為Field被注入
    protected final boolean isField;
    // 屬性描述,javabeans中的介面
    protected final PropertyDescriptor pd;
}

其中有兩個實現被作為AutowiredAnnotationBeanPostProcessor其內部類,AutowiredFieldElement和AutowiredMethodElement。前者代表被注入的域資訊,後者代表被注入的方法資訊。

關於資料結構的示意圖如下:

自動注入的註解@Autowired、@Value等最終將會解析成如上的結構,被injectionMetadataCache成員持有。所以AutowiredAnnotationBeanPostProcessor中將會有所有Bean的註解式自動配置元資訊。關於具體實現看以下分析:

// 後置處理合並的BeanDefinition,解析@Autowired和@Value等註解配置
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 如果beanType不為空,則查詢解析@Autowired註解配置,將抽象成InjectionMetadata,用其描述注入元資訊
    if (beanType != null) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }
}

這裡的核心邏輯便是findAutowiringMetadata,根據beanType尋找其自動裝配的元配置資訊:

// 因為解析class,查詢解析註解配置資訊是耗效能且這些配置資訊比較固定,所以這裡使用快取,將解析的註解配置資訊快取在
// injectionMetadataCache中
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // 獲取快取的key,如果存在beanName,則將beanName作為快取key;如果不存在,則將類名作為快取key
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // 從快取中獲取註解配置元資訊
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    // 如果沒有快取或者型別不匹配,則重新獲取
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        // 執行緒同步
        synchronized (this.injectionMetadataCache) {
            // 使用雙重檢查,再次獲取
            metadata = this.injectionMetadataCache.get(cacheKey);
            // 如果沒有或者型別不匹配,則重新獲取
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                try {
                    // 構建自動注入的配置資訊
                    metadata = buildAutowiringMetadata(clazz);
                    // 將其快取
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
                catch (NoClassDefFoundError err) {
                    throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                            "] for autowiring metadata: could not find class that it depends on", err);
                }
            }
        }
    }
    return metadata;
}

這個查詢邏輯非常簡單,就是從快取中獲取,如果沒有則重新構建自動注入的配置資訊。但是這裡的設計思想卻是非常經典。

Notes:
快取的設計思想在日常應用的開發中被大量使用,一般模式都遵循先從快取獲取,如果快取沒有,則從資料來源獲取,再填充快取。

對於配置資訊的具體解析構建則在buildAutowiringMetadata中實現:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    // 建立注入配置元素集合
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
    // 將當前需要解析的類作為目標類,即將解析
    Class<?> targetClass = clazz;
    // 迴圈遍歷解析當前類以及所有的父類
    do {
        // 建立解析類所對應的自動注入配置的集合
        final LinkedList<InjectionMetadata.InjectedElement> currElements =
                new LinkedList<InjectionMetadata.InjectedElement>();
        // 解析自動注入的域配置
        ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            }
        });
        // 解析自動注入的方法的配置
        ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static methods: " + method);
                        }
                        return;
                    }
                    if (method.getParameterTypes().length == 0) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            }
        });
        // 加入總個集合中
        elements.addAll(0, currElements);
        // 獲取父類,再次解析父類
        targetClass = targetClass.getSuperclass();
    }
    // 當類為空,或者是Object最頂層類時跳出迴圈
    while (targetClass != null && targetClass != Object.class);
    // 根據自動注入配置集合,構建自動注入配置物件
    return new InjectionMetadata(clazz, elements);
}

以上的邏輯也是非常簡單,就利用反射庫提供的API獲取class的所有Field和Method,然後解析其被標註的註解是否匹配自動注入註解型別,如果匹配則將其解析為AutowiredFieldElement和AutowiredMethodElement。

可以看其使用反射API的具體細節:

public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
    // 獲取class的所有域,迴圈遍歷處理其上註解
    for (Field field : getDeclaredFields(clazz)) {
        try {
            fc.doWith(field);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
        }
    }
}

public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
    // 獲取class中所有的方法,迴圈遍歷處理其上註解
    Method[] methods = getDeclaredMethods(clazz);
    for (Method method : methods) {
        try {
            mc.doWith(method);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
        }
    }
}

當然這裡使用了回撥的方式非常巧妙,使其更具有擴充套件性。再以具體的尋找域上的註解的例項詳解:

private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
    // 該ao上的註解不為空
    if (ao.getAnnotations().length > 0) {  // autowiring annotations have to be local
        // 遍歷自動處理註解型別
        for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
            // 獲取該ao上的自動處理註解(@Autowired, @Value, @Injected)
            AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
            // 如果註解屬性不為空,則返回
            if (attributes != null) {
                return attributes;
            }
        }
    }
    return null;
}

到這裡,讀者應該明白Spring中如何解析自動注入的註解式配置了。接下來便再從裝配的角度分析其原理:

@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    // 尋找自動裝配元配置,這個邏輯前面已經介紹
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        // 進行注入處理
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

以上邏輯非常簡單,根據自動裝入元配置進行注入:

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
    // 獲取需要注入的元素InjectedElement的集合
    Collection<InjectedElement> elementsToIterate =
            (this.checkedElements != null ? this.checkedElements : this.injectedElements);
    // 進行迭代注入
    if (!elementsToIterate.isEmpty()) {
        for (InjectedElement element : elementsToIterate) {
            if (logger.isDebugEnabled()) {
                logger.debug("Processing injected element of bean '" + beanName + "': " + element);
            }
            element.inject(target, beanName, pvs);
        }
    }
}

關於使用InjectedElement注入的邏輯由其實現類實現,這裡主要分析下AutowiredFieldElement根據域注入的實現方式:

@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    // 是域方式注入,將Member介面強轉成Field型別
    Field field = (Field) this.member;
    Object value;
    // 如果引數有被快取,則從快取中解析
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    // 否則重新解析依賴
    else {
        // 將field重新包裝成依賴描述
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        // 設定包含這個依賴的bean的class
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
        // 獲取轉換器
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // 獲取依賴的值,這裡使用了BeanFactory提供方法,這個方法之前的Bean建立中已經介紹,這裡不再贅述
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        // 同步,然後快取value值,並修改快取表示為true
        synchronized (this) {
            if (!this.cached) {
                if (value != null || this.required) {
                    this.cachedFieldValue = desc;
                    registerDependentBeans(beanName, autowiredBeanNames);
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                            this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                    desc, autowiredBeanName, field.getType());
                        }
                    }
                }
                else {
                    this.cachedFieldValue = null;
                }
                this.cached = true;
            }
        }
    }
    // 使用反射方式將值注入
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

其中BeanFactory的resolveDependency解析依賴中使用到了QualifierAnnotationAutowireCandidateResolver解析器,該解析器實現瞭解析Field中的@Value註解獲取值,如果value值不為空則當做依賴返回。關於這些細節由於篇幅原因這裡不再詳述。

總結

本文主要針對Spring中的註解式配置@Component系和@Autowired的作用原理從原始碼實現的角度進行深層次的剖析。對於這兩項基本功能,Spring都是以外掛的形式提供擴充套件的,主要基於Spring上下文核心提供的執行解析BeanDefinitionParser能力和Bean的例項化裝配能力。
其中需要重點掌握的是ComponentScanBeanDefinitionParser和AutowiredAnnotationBeanPostProcessor兩大元件。它們負責完成了本文的提到的註解式配置。