@EnableAutoConfiguration處理邏輯

4.spring,springBoot事件(最晚20200719)
5.自定義springboot starter(最晚20200726)

引言

工做中,咱們直接或間接的,用到@EnableAutoConfiguration註解。

今天,咱們就聊聊@EnableAutoConfiguration的處理邏輯。

找核心方法

@Enable開頭的的註解,上通常都有@Import用於指定要註解的邏輯實現類。
@EnableAutoConfiguration上的@Import,導入的是 AutoConfigurationImportSelector。
AutoConfigurationImportSelector就是要找的入口類。 類關係以下:
Aware系列都是用於注入響應的資源,Ordered用於排序。
值得關注的是 DeferredImportSelector,查看其類註釋,簡要翻譯以下:

importselector的變體,在全部@Configuration bean以後運行,能夠實現Ordered進行排序。spring

提供{getImportGroup(),它能夠跨不一樣的選擇器提供額外的排序和過濾邏輯。springboot

DeferredImportSelector保證在全部@Configuration加載以後執行,也就說,若是有相關配置類已加載,則能夠跳過自動裝配類。
DeferredImportSelector是如何保證在@Configuration bean加載以後執行的呢???
帶着這個疑問,我查看了ConfigurationClassPostProcessor#processConfigBeanDefinitions
(至於爲何要查看這個方法,請看上一篇 @Enable驅動原理)
瀏覽過程以下:




概要邏輯以下:bash


1. ImportSelector的解析在ConfigurationClassParser#processImports中處理
在其中this.deferredImportSelectorHandler.handle(..)j將DeferredImportSelector放入隊列,延後處理。
2. DeferredImportSelector處理邏輯在
ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。
瀏覽this.deferredImportSelectorHandler.process()代碼;



DeferredImportSelectorGrouping#getImports的代碼以下:ide


private static class DeferredImportSelectorGrouping {
    private final DeferredImportSelector.Group group;
   public Iterable<Group.Entry> getImports() {
   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
   }
   return this.group.selectImports();
}     
} 
}複製代碼
這裏須要關注的是this.group.process,this.group.selectImports2個方法。
也就是AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是咱們須要關注的核心方法。


逐個分析

process

public class AutoConfigurationImportSelector {
private static class AutoConfigurationGroup{
    //省略其餘代碼
  private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();  
@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()));
   AutoConfigurationEntry autoConfigurationEntry = 
   ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(getAutoConfigurationMetadata(),
                          annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}
}複製代碼
概要邏輯:
1.getAutoConfigurationMetadata() 加載autoConfigurationMetadata,
2.getAutoConfigurationEntry()根據autoConfigurationMetadata得到AutoConfigurationEntry
3.經過AutoConfigurationEntry ,得到要導入的類的名稱,存入內部的 autoConfigurationEntries中

1. getAutoConfigurationMetadata() 加載autoConfigurationMetadata

public class AutoConfigurationImportSelector {
private static class AutoConfigurationGroup{
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
   if (this.autoConfigurationMetadata == null) {
      this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
   }
   return this.autoConfigurationMetadata;
}
}
}複製代碼
核心就是AutoConfigurationMetadataLoader.loadMetadata 查看其源碼以下:
final class AutoConfigurationMetadataLoader {
   protected static final String PATH
            = "META-INF/" + "spring-autoconfigure-metadata.properties";
   private AutoConfigurationMetadataLoader() {
   }
   public static AutoConfigurationMetadata loadMetadata
                                   (ClassLoader classLoader) {
      return loadMetadata(classLoader, PATH);
   }
   static AutoConfigurationMetadata loadMetadata
                       (ClassLoader classLoader, String path) {
      try {
         Enumeration<URL> urls = (classLoader != null) 
                         ? classLoader.getResources(path)
                           : ClassLoader.getSystemResources(path);
         Properties properties = new Properties();
         while (urls.hasMoreElements()) {
            properties.putAll(
                PropertiesLoaderUtils.loadProperties(
                                new UrlResource(urls.nextElement())));
         }
         return loadMetadata(properties);
      }
      catch (IOException ex) {
         throw new
          IllegalArgumentException(
             "Unable to load @ConditionalOnClass location [" + path + "]",
                                                                      ex);
      }
   }
   //省略部分代碼
 }   複製代碼
加載autoConfigurationMetadata,就是讀取META-INF/spring-autoconfigure-metadata.properties的配置文件,轉換爲autoConfigurationMetadata對象。




2.根據autoConfigurationMetadata得到AutoConfigurationEntry




getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)的實現以下post


public class AutoConfigurationImportSelector{
    //省略其餘代碼
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
}複製代碼
概要邏輯:
  • getAttributes獲取註解的屬性
  • getCandidateConfiguration獲取候選裝配組件
  • removeDuplicates刪除重複的配置項
  • getExclusions 獲取排除的組件
  • spring.autoconfigure.exclude,exclude,excludeName對應的值存儲到set中
  • checkExcludedClasses 當前類在classLoader中,可是不在候選列表中內拋出異常
  • configurations.removeAll(exclusions)移除須要排除的配置項
  • filter 過濾不知足條件的自動裝配組件
  • fireAutoConfigurationImportEvents 發送@EnableAutoConfiguration的自動裝配事件

getCandidateConfiguration獲取候選裝配組件

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}複製代碼
查看 SpringFactoriesLoader源碼以下:
public final class SpringFactoriesLoader {
    //省略部分代碼
 public static final String FACTORIES_RESOURCE_LOCATION 
                                     = "META-INF/spring.factories";
 
 public static List<String> loadFactoryNames(
             Class<?> factoryClass, @Nullable ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   return loadSpringFactories(classLoader)
                   .getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(
                                @Nullable ClassLoader classLoader) {
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }
   try {
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils
                                         .loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryClassName = ((String) entry.getKey()).trim();
            for (String factoryName : StringUtils
                        .commaDelimitedListToStringArray(
                                    (String) entry.getValue())) {
               result.add(factoryClassName, factoryName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException(
                  "Unable to load factories from location [" +
                            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}                
}    複製代碼
讀取指定ClassLoader下的全部的,META-INF/spring.factories資源內容,合併一個key爲全類名,Value爲實現類名列表的Map,從Map中找到指定的key對應的實現類全類名列表


filter 過濾不知足條件的自動裝配組件


public class AutoConfigurationImportSelector{
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
   long startTime = System.nanoTime();
   String[] candidates = StringUtils.toStringArray(configurations);
   boolean[] skip = new boolean[candidates.length];
   boolean skipped = false;
   for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
      invokeAwareMethods(filter);
      boolean[] match = filter.match(candidates, autoConfigurationMetadata);
      for (int i = 0; i < match.length; i++) {
         if (!match[i]) {
            skip[i] = true;
            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);
}
}複製代碼
AutoConfigurationImportFilter#match沒有匹配上的過濾掉
查看getAutoConfigurationImportFilters()源碼以下:
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
   return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}複製代碼
SpringFactoriesLoader.loadFactories(...),調用SpringFactoriesLoader.loadFactoryNames(...)後,將列表中的逐個實例化,排序後返回。
查看spring.factories


AutoConfigurationImportFilter的實現類,有
OnClassCondition類,onBeanCondition,OnWebApplicationCondition等。

擴展-查閱OnClassCondition的match方法


OnClassCondition代碼大體邏輯以下:
OnClassCondition#match調用了autoConfigurationMetadata.getSet獲取當前配置類的ConditionOnClass屬性。
舉例說明OnClassCondition#getOutcomes
例如:
加載AConfigurationClass,AConfigurationClass的ConditionOnClass=XXX,
若是XXX不在當前classloader下,排除 AConfigurationClass

selectImports

public class AutoConfigurationImportSelector{
private static class AutoConfigurationGroup{
@Override
public Iterable<Entry> selectImports() {
   if (this.autoConfigurationEntries.isEmpty()) {
      return Collections.emptyList();
   }
   Set<String> allExclusions = this.autoConfigurationEntries.stream()
         .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
   Set<String> processedConfigurations = 
   this.autoConfigurationEntries.stream()
         .map(AutoConfigurationEntry::getConfigurations)
         .flatMap(Collection::stream)
         .collect(Collectors.toCollection(LinkedHashSet::new));
   processedConfigurations.removeAll(allExclusions);

   return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
         .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
         .collect(Collectors.toList());
}
}
}
}
}複製代碼
概要邏輯以下
排除沒必要要的項
sortAutoConfigurations排序自動配置項。

查看sortAutoConfigurations

public class AutoConfigurationImportSelector{
private static class AutoConfigurationGroup{
private List<String> sortAutoConfigurations(Set<String> configurations,
      AutoConfigurationMetadata autoConfigurationMetadata) {
   return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
         .getInPriorityOrder(configurations);
}
}
}
複製代碼
核心在AutoConfigurationSorter#getInPriorityOrder中
class AutoConfigurationSorter {
    //省略其餘代碼
public List<String> getInPriorityOrder(Collection<String> classNames) {
   AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
         this.autoConfigurationMetadata, classNames);
   List<String> orderedClassNames = new ArrayList<>(classNames);
   // Initially sort alphabetically
   Collections.sort(orderedClassNames);
   // Then sort by order
   orderedClassNames.sort((o1, o2) -> {
      int i1 = classes.get(o1).getOrder();
      int i2 = classes.get(o2).getOrder();
      return Integer.compare(i1, i2);
   });
   // Then respect @AutoConfigureBefore @AutoConfigureAfter
   orderedClassNames = sortByAnnotation(classes, orderedClassNames);
   return orderedClassNames;
}
}複製代碼
排序規則以下:
按照字母順序加載,不然先按AutoCOnfigureOrder,再按@AutoConfigureBefore,@AutoConfigureAfter進行排序。
注:
AutoCOnfigurationMetadata(META-INF/spring-autoconfigure-metadata.properties)中包含AutoConfigureOrder,AutoConfigureBefore,AutoConfigureAfter信息
名稱

註釋

是否推薦
@AutoCOnfigureOrder 絕對自動裝配順序
@AutoConfigureBefore,
@AutoConfigureAfter
相對自動裝配順序,建議使用name屬性
總體流程到這裏就看完了,可是自定義配置如何覆蓋自動裝配的呢?

查漏補缺

爲了查找自定義配置如何覆蓋自動裝配,我再次翻閱代碼


查看shouldSkip,結合各類Conditional註解進行過濾
@Conditional 是個多個條件註解的元註解,例如:@ConditionalOnMissingBean,@ConditionalOnClass等。
具體邏輯,就不在這裏贅述了,若是想查閱,能夠查看org.springframework.boot.autoconfigure.condition.OnClassCondition這個類,它是@ConditionalOnClass的邏輯處理類。

總結

  1. @EnableAutoConfiguration的核心處理類是AutoConfigurationImportSelector
  2. AutoConfigurationImportSelector實現了DeferredImportSelector
  3. ImportSelector的解析在ConfigurationClassParser#processImports中處理在其中this.deferredImportSelectorHandler.handle(..)j將DeferredImportSelector放入隊列,延後處理。DeferredImportSelector處理邏輯在ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。
  4. AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是咱們須要關注的核心方法。
  5. ConfigurationClassParser#doProcessConfigurationClass中的!this.conditionEvaluator.shouldSkip(...) 結合了各類Condition註解,實現了自定義配置覆蓋自動裝配。
相關文章
相關標籤/搜索