SpringBoot自動(dòng)裝配機(jī)制分析

??上篇分析了SpringBoot應(yīng)用的啟動(dòng)流程,說(shuō)到過(guò)自動(dòng)裝配發(fā)生在加載配置類(lèi)并解析出對(duì)應(yīng)的BeanDefinition這一階段,同時(shí)我們也簡(jiǎn)要說(shuō)明了@EnableAutoConfiguration是通過(guò)@Import元注解引入的AutoConfigurationImportSelector來(lái)實(shí)現(xiàn)的,今天就來(lái)分析下吧(基于spring-boot-2.1.6.RELEASE)。

Preface

package

??典型的SpringBoot應(yīng)用大多有著類(lèi)似上圖的包結(jié)構(gòu),我們知道@SpringBootApplication是一個(gè)復(fù)合注解,它包括:

  1. @Configuration
  2. @ComponentScan
  3. @EnableAutoConfiguration

這就意味著左側(cè)Application這個(gè)類(lèi)可以作為配置類(lèi)來(lái)定義組件,比如添加@Bean標(biāo)注的方法;同時(shí)它所在的包com.anyoptional作為組件掃描的根包,所有定義在其下及其子包下的組件都會(huì)被Spring IoC容器管理;最后是通過(guò)自動(dòng)裝配機(jī)制導(dǎo)入的外部配置。

??再提一嘴@Import元注解,它支持的導(dǎo)入類(lèi)型有三種:

  1. @Configuration配置類(lèi),直接向容器導(dǎo)入組件
  2. ImportSelector實(shí)現(xiàn)類(lèi),返回@Configuration配置類(lèi),間接向容器導(dǎo)入組件
  3. ImportBeanDefinitionRegistrar實(shí)現(xiàn)類(lèi),攜帶BeanDefinitionRegistry,支持直接向容器中注冊(cè)組件

How

??AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector接口——ImportSelector的一個(gè)變種,它的運(yùn)行時(shí)機(jī)在所有@Configuration配置類(lèi)被處理完畢之后。注意這個(gè)時(shí)間點(diǎn),此時(shí)所有用戶自定義的組件已經(jīng)得到解析,這不正是條件注解@Conditional生效的好時(shí)機(jī)嗎?大家比較熟悉的@ConditionalOnMissingBean、@ConditionalOnProperty均是@Conditional元注解衍生出來(lái)的。spring-boot-autoconfigure雖然大量依賴(lài)@Conditional來(lái)實(shí)現(xiàn)條件注入,不過(guò)這部分邏輯卻是在spring-context中實(shí)現(xiàn)的,有興趣的話大家可以翻翻ConfigurationClassPostProcessor的源碼。

selectImports(...)

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 查看配置項(xiàng) spring.boot.enableautoconfiguration
    // 看看是否允許自動(dòng)裝配
   if (!isEnabled(annotationMetadata)) {
       // 不允許的話返回空數(shù)組了事
      return NO_IMPORTS;
   }
    // 解析由 auto-configure annotation processor 生成的元數(shù)據(jù)
    // 默認(rèn)位于 META-INF/spring-autoconfigure-metadata.properties
    // 里面的信息主要有兩部分:
    //  1. 自動(dòng)配置類(lèi)的導(dǎo)入條件,用來(lái)做過(guò)濾,優(yōu)點(diǎn)是不用實(shí)例化配置類(lèi)再做判斷
    //  2. 自動(dòng)配置類(lèi)的相對(duì)順序,用來(lái)調(diào)整加載順序
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
    // AutoConfigurationEntry 包含需要導(dǎo)入的自動(dòng)配置類(lèi)和需要排除的自動(dòng)配置類(lèi)
    // 這一步便是通過(guò) Spring SPI 機(jī)制加載所有的自動(dòng)配置類(lèi),當(dāng)然這里面還包含
    // 過(guò)濾、排序等一系列操作
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
         annotationMetadata);
    // 返回需要由容器加載的自動(dòng)配置類(lèi)
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
AutoConfigurationMetadataLoader

??AutoConfigurationMetadataLoader的源碼并不復(fù)雜,默認(rèn)情況下它讀取并解析位于META-INF下的spring-autoconfigure-metadata.properties文件。這個(gè)文件由AutoConfigureAnnotationProcessor解析class file生成(位于包spring-boot-autoconfigure-processor),它主要包含以下兩個(gè)方面的內(nèi)容:

  1. 自動(dòng)配置類(lèi)的導(dǎo)入條件
  2. 自動(dòng)配置類(lèi)的相對(duì)順序
# 截取自 spring-boot-autoconfifure 下的 META-INF/spring-autoconfigure-metadata.properties
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.ConditionalOnSingleCandidate=javax.sql.DataSource

對(duì)于上面這個(gè)例子,說(shuō)明:

  1. JdbcTemplateAutoConfiguration需要在DataSourceAutoConfiguration之后加載
  2. JdbcTemplateAutoConfiguration是否生效取決于容器中是否存在唯一的類(lèi)型為javax.sql.DataSourceBean

這其實(shí)很好理解,因?yàn)?code>JdbcTemplate操作數(shù)據(jù)庫(kù)的能力來(lái)源于標(biāo)準(zhǔn)的javax.sql.DataSource,而javax.sql.DataSource默認(rèn)情況下由DataSourceAutoConfiguration提供。

// AutoConfigureAnnotationProcessor 的聲明
@SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration",
        "org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
        "org.springframework.boot.autoconfigure.condition.ConditionalOnBean",
        "org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate",
        "org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication",
        "org.springframework.boot.autoconfigure.AutoConfigureBefore",
        "org.springframework.boot.autoconfigure.AutoConfigureAfter",
        "org.springframework.boot.autoconfigure.AutoConfigureOrder" })
public class AutoConfigureAnnotationProcessor extends AbstractProcessor { 
    // omitted...
}

翻一下AutoConfigureAnnotationProcessor的源碼,可以清楚地看到它支持的注解類(lèi)型。因?yàn)?code>AutoConfigureAnnotationProcessor解析的是class file,所以可以實(shí)現(xiàn)在不實(shí)例化自動(dòng)配置類(lèi)(比如上例的JdbcTemplateAutoConfiguration)的情況下判斷它是否可以被容器加載,算是對(duì)內(nèi)存的一點(diǎn)節(jié)約吧。

AutoConfigurationEntry
protected static class AutoConfigurationEntry {
    // 需要被加載的自動(dòng)配置類(lèi)
    private final List<String> configurations;
    // 不需要被加載的自動(dòng)配置類(lèi)
    private final Set<String> exclusions;
    
    // rest are omitted...
}

protected AutoConfigurationEntry getAutoConfigurationEntry(
    AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
    // 獲取 @EnableAutoConfiguration 注解 屬性 -> 屬性值 的映射
    // 其實(shí)就是聲明的 exclude/excludeName
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 通過(guò) Spring SPI 機(jī)制,獲取 spring.factories 文件中所有以
    // @EnableAutoConfiguration 注解的全限定名為 key 的值,這些值對(duì)應(yīng)著自動(dòng)配置類(lèi)的名稱(chēng)
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 通過(guò) Set 去重
   configurations = removeDuplicates(configurations);
    // 提取由 @EnableAutoConfiguration#exclude/excludeName 和 spring.autoconfigure.exclude
    // 指定的自動(dòng)配置類(lèi)的名稱(chēng),這些配置類(lèi)不需要被容器加載
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 檢查這些待排除的自動(dòng)配置類(lèi)是否存在,并且它們是否真的屬于自動(dòng)配置類(lèi)的一員
   checkExcludedClasses(configurations, exclusions);
    // 從自動(dòng)配置類(lèi)集合中排除這些不需要加載的
   configurations.removeAll(exclusions);
    // 根據(jù) AutoConfigurationMetadata 進(jìn)行過(guò)濾
   configurations = filter(configurations, autoConfigurationMetadata);
    // 通過(guò) SPI 機(jī)制加載 AutoConfigurationImportListener(s) 進(jìn)行回調(diào)
   fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回結(jié)果
   return new AutoConfigurationEntry(configurations, exclusions);
}

??getAutoConfigurationEntry(...)主要做了下面幾件事:

  1. 匯總需要排除的自動(dòng)配置類(lèi)
  2. 通過(guò)Spring SPI機(jī)制提取所有待加載的自動(dòng)配置類(lèi)
  3. 過(guò)濾第2步得到的集合,剔除不符合要求或不需要進(jìn)行加載的
  4. 回調(diào)AutoConfigurationImportListener,監(jiān)聽(tīng)器同樣由Spring SPI機(jī)制加載

其中第12和第4步都比較簡(jiǎn)單,大家自己看看就可以了,我們重點(diǎn)來(lái)說(shuō)說(shuō)第3步——過(guò)濾。

private List<String> filter(List<String> configurations, 
                            AutoConfigurationMetadata autoConfigurationMetadata) {
   long startTime = System.nanoTime();
   String[] candidates = StringUtils.toStringArray(configurations);
    // 對(duì)應(yīng) configurations 中自動(dòng)配置類(lèi)的下標(biāo),標(biāo)記其是否需要被排除
   boolean[] skip = new boolean[candidates.length];
   boolean skipped = false;
    // 通過(guò) SPI 機(jī)制加載 AutoConfigurationImportFilter 來(lái)進(jìn)行過(guò)濾
   for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
      invokeAwareMethods(filter);
       // 判定
      boolean[] match = filter.match(candidates, autoConfigurationMetadata);
      for (int i = 0; i < match.length; i++) {
          // 未通過(guò)的話進(jìn)行記錄
         if (!match[i]) {
            skip[i] = true;
             // 清掉數(shù)據(jù),如此下一個(gè) Filter 就不用再判定了
            candidates[i] = null;
            skipped = true;
         }
      }
   }
    // 都有效直接返回即可,否則...
   if (!skipped) {
      return configurations;
   }
    // 就需要遍歷一次,剔除不需要的
   List<String> result = new ArrayList<>(candidates.length);
   for (int i = 0; i < candidates.length; i++) {
      if (!skip[i]) {
         result.add(candidates[i]);
      }
   }
   if (logger.isTraceEnabled()) {
      int numberFiltered = configurations.size() - result.size();
      logger.trace("Filtered " + numberFiltered + " auto configuration class in "
            + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
   }
   return new ArrayList<>(result);
}

可以看到對(duì)自動(dòng)配置類(lèi)的過(guò)濾工作是通過(guò)代理給AutoConfigurationImportFilter來(lái)完成的,這些過(guò)濾器同樣是通過(guò)Spring SPI機(jī)制進(jìn)行加載的,默認(rèn)注冊(cè)的有:

# 截取自 spring-boot-autoconfigure 包下的 META-INF/spring.factories
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

都是好東西,挑一挑吧。那就挑OnClassCondition來(lái)看看吧。

OnClassCondition

??翻開(kāi)OnClassCondition的源碼,可以看到AutoConfigurationImportFilter#match(...)是其父類(lèi)實(shí)現(xiàn)的,然后代理給了模板方法getOutcomes(...),我們直接看這個(gè)方法就行。

// 實(shí)際工作又代理給了 OutcomesResolver, 注意這里有個(gè)并行的優(yōu)化
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, 
                                               AutoConfigurationMetadata autoConfigurationMetadata) {
    // 天下自動(dòng)配置類(lèi)千千萬(wàn)
    int split = autoConfigurationClasses.length / 2;
    // 你一半
    OutcomesResolver firstHalfResolver = createOutcomesResolver(autoConfigurationClasses, 0, split,
    autoConfigurationMetadata);
    // 我一半
    OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(autoConfigurationClasses, split,
    autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
    // 合并兩個(gè) OutcomesResolver 的處理結(jié)果
    ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
    ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
    ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
    System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
    System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
    return outcomes;
}

只能說(shuō)大牛寫(xiě)的代碼都比較繞吧,getOutcomes(...)也是個(gè)代理商,實(shí)際的工作都交給OutcomesResolver去完成了。注意這里分而治之的思想,待處理的自動(dòng)配置類(lèi)被一分為二,由兩個(gè)線程并行完成。那繼續(xù)看看StandardOutcomesResolver是怎么處理的唄。

private final class StandardOutcomesResolver implements OutcomesResolver {

    // fields、ctor are omitted...

   @Override
   public ConditionOutcome[] resolveOutcomes() {
      return getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata);
   }

   private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
         AutoConfigurationMetadata autoConfigurationMetadata) {
      ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
      for (int i = start; i < end; i++) {
         String autoConfigurationClass = autoConfigurationClasses[i];
         if (autoConfigurationClass != null) {
             // 獲取當(dāng)前 autoConfigurationClass 的 ConditionalOnClass
             // 比如 example.MyConfig.ConditionalOnClass=example.YourConfig
             // 這里就會(huì)返回 example.YourConfig,注意這個(gè)值是 CSV 格式的
            String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
            if (candidates != null) {
                // 判定 candidates 代表的類(lèi)是否存在于 classpath 下
               outcomes[i - start] = getOutcome(candidates);
            }
         }
      }
      return outcomes;
   }
   // 對(duì) candidates 的格式進(jìn)行處理
   private ConditionOutcome getOutcome(String candidates) {
      try {
         if (!candidates.contains(",")) {
            return getOutcome(candidates, this.beanClassLoader);
         }
          // CSV 格式分解
         for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
            ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
            if (outcome != null) {
               return outcome;
            }
         }
      }
      catch (Exception ex) {
         // We'll get another chance later
      }
      return null;
   }
    // 最終的判定邏輯
   private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
       // ClassNameFilter 無(wú)非是通過(guò)類(lèi)加載機(jī)制來(lái)進(jìn)行判定
       // 比如 classLoader.loadClass(className) 或者 classLoader 為空則是 Class.forName(className)
      if (ClassNameFilter.MISSING.matches(className, classLoader)) {
         return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
               .didNotFind("required class").items(Style.QUOTE, className));
      }
      return null;
   }
}

前面分析了AutoConfigurationMetadata的加載,這里就利用上了這部分元數(shù)據(jù)——由autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass")體現(xiàn),剩下的只需要通過(guò)類(lèi)加載機(jī)制判定一下這些類(lèi)是否存在就可以了。注意整個(gè)判定過(guò)程中自動(dòng)配置類(lèi)并沒(méi)有實(shí)例化,僅僅使用了它的全限定名。

getImportGroup()

??前面對(duì)selectImports(...)的分析已經(jīng)可以解釋SpringBoot的自動(dòng)裝配機(jī)制了,不過(guò)這里還有一個(gè)問(wèn)題,那就是沒(méi)有處理自動(dòng)配置類(lèi)的加載順序。我們知道自動(dòng)配置類(lèi)可以使用@AutoConfigureBefore@AutoConfigureAfter來(lái)指定相對(duì)順序,使用AutoConfigureOrder來(lái)指定絕對(duì)順序,不過(guò)這在selectImports(...)中一點(diǎn)兒也沒(méi)有得到體現(xiàn)。答案就在getImportGroup(),它返回的AutoConfigurationGroup處理了這個(gè)遺留問(wèn)題。

@Override
public Class<? extends Group> getImportGroup() {
    return AutoConfigurationGroup.class;
}
AutoConfigurationGroup
private static class AutoConfigurationGroup
      implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {

    // fields、*Aware are omitted...

   @Override
   public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
      Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
            () -> String.format("Only %s implementations are supported, got %s",
                  AutoConfigurationImportSelector.class.getSimpleName(),
                  deferredImportSelector.getClass().getName()));
       // 上一節(jié)分析過(guò)了,這里就得到了待加載和被排除的自動(dòng)配置類(lèi)集合
      AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
      this.autoConfigurationEntries.add(autoConfigurationEntry);
       // 自動(dòng)裝配類(lèi)的類(lèi)名 -> 標(biāo)注 @EnableAutoConfiguration 類(lèi)的注解元信息
      for (String importClassName : autoConfigurationEntry.getConfigurations()) {
         this.entries.putIfAbsent(importClassName, annotationMetadata);
      }
   }

   @Override
   public Iterable<Entry> selectImports() {
      if (this.autoConfigurationEntries.isEmpty()) {
         return Collections.emptyList();
      }
      Set<String> allExclusions = this.autoConfigurationEntries.stream()
          // 待排除的自動(dòng)配置類(lèi)集合  
          .map(AutoConfigurationEntry::getExclusions)
          // 降維
          .flatMap(Collection::stream)
          // 收集成 Set 去重
          .collect(Collectors.toSet());
      Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
            // 待加載的自動(dòng)配置類(lèi)集合 
            .map(AutoConfigurationEntry::getConfigurations)
            // 降維
            .flatMap(Collection::stream)
            // 收集成 Set 去重并保持順序
            .collect(Collectors.toCollection(LinkedHashSet::new));
       // 剔除明確不需要加載的
      processedConfigurations.removeAll(allExclusions);
      // 對(duì)剩下的進(jìn)行排序并轉(zhuǎn)換成Entry
      return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
   }

  // getAutoConfigurationMetadata() omitted...

   private List<String> sortAutoConfigurations(Set<String> configurations,
         AutoConfigurationMetadata autoConfigurationMetadata) {
       // 使用 AutoConfigurationSorter 進(jìn)行排序
      return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
            .getInPriorityOrder(configurations);
   }

    // 如果 spring boot starter 未引入 spring-boot-autoconfigure-processor.jar
    // 那就不會(huì)生成 spring-autoconfigure-metadata.properties
    // 這樣的話就需要 MetadataReaderFactory 創(chuàng)建 MetadataReader 來(lái)讀取
    // 和自動(dòng)配置順序相關(guān)的注解了,比如 @AutoConfigureBefore
   private MetadataReaderFactory getMetadataReaderFactory() {
      try {
         return this.beanFactory.getBean(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
               MetadataReaderFactory.class);
      } catch (NoSuchBeanDefinitionException ex) {
         return new CachingMetadataReaderFactory(this.resourceLoader);
      }
   }
}

AutoConfigurationGroup確實(shí)只加入了對(duì)自動(dòng)配置類(lèi)順序的處理,其它邏輯是相同的,我們的重心自然也轉(zhuǎn)到AutoConfigurationSorter

AutoConfigurationSorter
public List<String> getInPriorityOrder(Collection<String> classNames) {
   AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
         this.autoConfigurationMetadata, classNames);
   List<String> orderedClassNames = new ArrayList<>(classNames);
   // 1. 按字母表順序排序
   Collections.sort(orderedClassNames);
   // 2. 按 @AutoConfigureOrder 指定的優(yōu)先級(jí)排序
   orderedClassNames.sort((o1, o2) -> {
      int i1 = classes.get(o1).getOrder();
      int i2 = classes.get(o2).getOrder();
      return Integer.compare(i1, i2);
   });
   // 3. 最后根據(jù) @AutoConfigureBefore @AutoConfigureAfter 來(lái)進(jìn)行微調(diào)
   orderedClassNames = sortByAnnotation(classes, orderedClassNames);
   return orderedClassNames;
}

??鑒于本篇的篇幅已經(jīng)很長(zhǎng)了,這里僅僅標(biāo)注了一下AutoConfigurationSorter進(jìn)行排序的大體步驟,具體細(xì)節(jié)各位看官自己去扣一扣吧。

End

??今天和大家分享了SpringBoot自動(dòng)裝配機(jī)制的原理,相信大家看完后可以開(kāi)心地編排自定義的starter了,完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容