Spring Boot 中的 @EnableAutoConfiguration 是如何處理的?
點(diǎn)擊上方藍(lán)色“程序猿DD”,選擇“設(shè)為星標(biāo)”
回復(fù)“資源”獲取獨(dú)家整理的學(xué)習(xí)資料!

作者 | 溫安適
引言
工作中,我們直接或間接的,用到@EnableAutoConfiguration注解。

今天,我們就聊聊@EnableAutoConfiguration的處理邏輯。
找核心方法
@Enable開頭的的注解,上一般都有@Import用于指定要注解的邏輯實(shí)現(xiàn)類。
@EnableAutoConfiguration上的@Import,導(dǎo)入的是 AutoConfigurationImportSelector。
AutoConfigurationImportSelector就是要找的入口類。 類關(guān)系如下:

Aware系列都是用于注入響應(yīng)的資源,Ordered用于排序。
值得關(guān)注的是 DeferredImportSelector,查看其類注釋,簡要翻譯如下:
importselector的變體,在所有@Configuration bean之后運(yùn)行,可以實(shí)現(xiàn)Ordered進(jìn)行排序。
提供{getImportGroup(),它可以跨不同的選擇器提供額外的排序和過濾邏輯。
DeferredImportSelector保證在所有@Configuration加載之后執(zhí)行,也就說,如果有相關(guān)配置類已加載,則可以跳過自動裝配類。
DeferredImportSelector是如何保證在@Configuration bean加載之后執(zhí)行的呢???
帶著這個疑問,我查看了ConfigurationClassPostProcessor#processConfigBeanDefinitions
(至于為什么要查看這個方法,請看上一篇 @Enable驅(qū)動原理)
瀏覽過程如下:

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

DeferredImportSelectorGrouping#getImports的代碼如下:
private?static?class?DeferredImportSelectorGrouping?{
????private?final?DeferredImportSelector.Group?group;
???public?Iterable?getImports()?{
???for?(DeferredImportSelectorHolder?deferredImport?:?this.deferredImports)?{
??????this.group.process(deferredImport.getConfigurationClass().getMetadata(),
????????????deferredImport.getImportSelector());
???}
???return?this.group.selectImports();
}?????
}?
}
這里需要關(guān)注的是this.group.process,this.group.selectImports2個方法。
也就是AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我們需要關(guān)注的核心方法。
逐個分析
process
public?class?AutoConfigurationImportSelector?{
private?static?class?AutoConfigurationGroup{
????//省略其他代碼
??private?final?Map?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()根據(jù)autoConfigurationMetadata獲得AutoConfigurationEntry
3.通過AutoConfigurationEntry ,獲得要導(dǎo)入的類的名稱,存入內(nèi)部的 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?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的配置文件,轉(zhuǎn)換為autoConfigurationMetadata對象。
2.根據(jù)autoConfigurationMetadata獲得AutoConfigurationEntry
getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)的實(shí)現(xiàn)如下
public?class?AutoConfigurationImportSelector{
????//省略其他代碼
protected?AutoConfigurationEntry?getAutoConfigurationEntry(AutoConfigurationMetadata?autoConfigurationMetadata,
??????AnnotationMetadata?annotationMetadata)?{
???if?(!isEnabled(annotationMetadata))?{
??????return?EMPTY_ENTRY;
???}
???AnnotationAttributes?attributes?=?getAttributes(annotationMetadata);
???List?configurations?=?getCandidateConfigurations(annotationMetadata,?attributes);
???configurations?=?removeDuplicates(configurations);
???Set?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刪除重復(fù)的配置項 getExclusions 獲取排除的組件 spring.autoconfigure.exclude,exclude,excludeName對應(yīng)的值存儲到set中 checkExcludedClasses 當(dāng)前類在classLoader中,但是不在候選列表中內(nèi)拋出異常 configurations.removeAll(exclusions)移除需要排除的配置項 filter 過濾不滿足條件的自動裝配組件 fireAutoConfigurationImportEvents 發(fā)送@EnableAutoConfiguration的自動裝配事件
getCandidateConfiguration獲取候選裝配組件
protected?List?getCandidateConfigurations(AnnotationMetadata?metadata,?AnnotationAttributes?attributes)?{
???List?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?loadFactoryNames(
?????????????Class>?factoryClass,?@Nullable?ClassLoader?classLoader)?{
???String?factoryClassName?=?factoryClass.getName();
???return?loadSpringFactories(classLoader)
???????????????????.getOrDefault(factoryClassName,?Collections.emptyList());
}
private?static?Map>?loadSpringFactories(
????????????????????????????????@Nullable?ClassLoader?classLoader)?{
???MultiValueMap?result?=?cache.get(classLoader);
???if?(result?!=?null)?{
??????return?result;
???}
???try?{
??????Enumeration?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資源內(nèi)容,合并一個key為全類名,Value為實(shí)現(xiàn)類名列表的Map,從Map中找到指定的key對應(yīng)的實(shí)現(xiàn)類全類名列表
filter 過濾不滿足條件的自動裝配組件
public?class?AutoConfigurationImportSelector{
private?List?filter(List?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??????????if?(!match[i])?{
????????????skip[i]?=?true;
????????????candidates[i]?=?null;
????????????skipped?=?true;
?????????}
??????}
???}
???if?(!skipped)?{
??????return?configurations;
???}
???List?result?=?new?ArrayList<>(candidates.length);
???for?(int?i?=?0;?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?getAutoConfigurationImportFilters()?{
???return?SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,?this.beanClassLoader);
}
SpringFactoriesLoader.loadFactories(...),調(diào)用SpringFactoriesLoader.loadFactoryNames(...)后,將列表中的逐個實(shí)例化,排序后返回。
查看spring.factories

AutoConfigurationImportFilter的實(shí)現(xiàn)類,有
OnClassCondition類,onBeanCondition,OnWebApplicationCondition等。
擴(kuò)展-查閱OnClassCondition的match方法

OnClassCondition代碼大致邏輯如下:
OnClassCondition#match調(diào)用了autoConfigurationMetadata.getSet獲取當(dāng)前配置類的ConditionOnClass屬性。
舉例說明OnClassCondition#getOutcomes
例如:
加載AConfigurationClass,AConfigurationClass的ConditionOnClass=XXX,
如果XXX不在當(dāng)前classloader下,排除 AConfigurationClass
selectImports
public?class?AutoConfigurationImportSelector{
private?static?class?AutoConfigurationGroup{
@Override
public?Iterable?selectImports()?{
???if?(this.autoConfigurationEntries.isEmpty())?{
??????return?Collections.emptyList();
???}
???Set?allExclusions?=?this.autoConfigurationEntries.stream()
?????????.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
???Set?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?sortAutoConfigurations(Set?configurations,
??????AutoConfigurationMetadata?autoConfigurationMetadata)?{
???return?new?AutoConfigurationSorter(getMetadataReaderFactory(),?autoConfigurationMetadata)
?????????.getInPriorityOrder(configurations);
}
}
}
核心在AutoConfigurationSorter#getInPriorityOrder中
class?AutoConfigurationSorter?{
????//省略其他代碼
public?List?getInPriorityOrder(Collection?classNames)?{
???AutoConfigurationClasses?classes?=?new?AutoConfigurationClasses(this.metadataReaderFactory,
?????????this.autoConfigurationMetadata,?classNames);
???List?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;
}
}
排序規(guī)則如下:
按照字母順序加載,否則先按AutoCOnfigureOrder,再按@AutoConfigureBefore,@AutoConfigureAfter進(jìn)行排序。
注:
AutoCOnfigurationMetadata(META-INF/spring-autoconfigure-metadata.properties)中包含AutoConfigureOrder,AutoConfigureBefore,AutoConfigureAfter信息
| 名稱 | 注釋 | 是否推薦 |
|---|---|---|
| @AutoCOnfigureOrder | 絕對自動裝配順序 | 否 |
| @AutoConfigureBefore,@AutoConfigureAfter | 相對自動裝配順序,建議使用name屬性 | 是 |
整體流程到這里就看完了,但是自定義配置如何覆蓋自動裝配的呢?
查漏補(bǔ)缺
為了查找自定義配置如何覆蓋自動裝配,我再次翻閱代碼

查看shouldSkip,結(jié)合各種Conditional注解進(jìn)行過濾
@Conditional 是個多個條件注解的元注解,例如:@ConditionalOnMissingBean,@ConditionalOnClass等。
具體邏輯,就不在這里贅述了,如果想查閱,可以查看org.springframework.boot.autoconfigure.condition.OnClassCondition這個類,它是@ConditionalOnClass的邏輯處理類。
總結(jié)
@EnableAutoConfiguration的核心處理類是AutoConfigurationImportSelector AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector ImportSelector的解析在ConfigurationClassParser#processImports中處理在其中this.deferredImportSelectorHandler.handle(..)j將DeferredImportSelector放入隊列,延后處理。DeferredImportSelector處理邏輯在ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。 AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我們需要關(guān)注的核心方法。 ConfigurationClassParser#doProcessConfigurationClass中的!this.conditionEvaluator.shouldSkip(...) 結(jié)合了各種Condition注解,實(shí)現(xiàn)了自定義配置覆蓋自動裝配。
往期推薦
掃一掃,關(guān)注我
一起學(xué)習(xí),一起進(jìn)步
