本文基于
Spring 5.1.7.release
SpringBoot 2.1.5.release
在之前的文章 SpringBoot 中的 Bean 定義是如何被注冊的攻人? 中已經(jīng)講過 bean 定義的加載和注冊過程,但你可能有個疑惑 SpringBoot 的 AutoConfiguration 配置類是如何被加載的呢距芬?這篇文章就帶你揭開這個面紗。
用過 SpringBoot 的朋友都知道循帐,SpringBoot 有一個重要的注解就是 @SpringBootApplication
框仔。奧秘就在這個注解的元注解 @EnableAutoConfiguration
上,看下 @EnableAutoConfiguration
的結(jié)構(gòu):
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
可以看到它的上面有個 @Import
的注解拄养,value = AutoConfigurationImportSelector.class离斩,它是一個 DeferredImportSelector(ImportSelector),如果你看過了 SpringBoot 中的 Bean 定義是如何被注冊的瘪匿? 你就應(yīng)該知道跛梗,它會在 ConfigurationClassParser#doProcessConfigurationClass#processImports 中被解析。另外它是一個 DeferredImportSelector 表示它是一個延后處理的 ImportSelector棋弥。
調(diào)用過程
1. ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 在這一步處理延后處理的 ImportSelector核偿,也就是實現(xiàn)了 DeferredImportSelector 的類
this.deferredImportSelectorHandler.process();
}
2. DeferredImportSelectorHandler#process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 處理
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
3. DeferredImportSelectorGroupingHandler#register
public void register(DeferredImportSelectorHolder deferredImport) {
// 獲取 group class類。AutoConfigurationImportSelector 返回的是一個 AutoConfigurationImportSelector&AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
// 放入 groupings顽染,記住這個 groupings漾岳,后面會用到
// 如果 group == null,則使用默認(rèn)的 group DefaultDeferredImportSelectorGroup
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
4. DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// grouping.getImports() 加載 AutoConfiguration
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
// 處理 import
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
這里是加載和注冊 AutoConfiguration 最重要的一步粉寞,所有的奧秘都在 grouping.getImports() 這個方法中尼荆。
grouping.getImports() 獲取所有的 AutoConfiguration 類,processImports 處理唧垦。
5. DeferredImportSelectorGrouping#getImports
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 處理所有的 AutoConfiguration
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 獲取
return this.group.selectImports();
}
這里的 group 是 AutoConfigurationGroup
6. AutoConfigurationGroup#process
@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()));
// 這一步獲取所有的 EnableAutoConfiguration
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
注意:getAutoConfigurationMetadata() 會獲取 spring-autoconfigure-metadata.properties 中的內(nèi)容用于過濾 AutoConfiguration
AutoConfigurationMetadataLoader#loadMetadata
// PATH = "META-INF/spring-autoconfigure-metadata.properties"
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
7. AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 判斷是否開啟了自動配置捅儒,可以在屬性里配置
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 獲取需要排除加載的類,在 @EnableAutoConfiguration 中配置
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 使用 SpringFactoriesLoader 加載 META-INF/spring.factories 文件中 EnableAutoConfiguration 的類
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 使用 Set 去重
configurations = removeDuplicates(configurations);
// 獲取需要排除加載的類名
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 校驗排除類是否是 auto-configuration 類业崖,如果不是則報錯野芒。
// 也就是說排除的類必須是 META-INF/spring.factories 文件中 EnableAutoConfiguration 的類
checkExcludedClasses(configurations, exclusions);
// 去除排除類
configurations.removeAll(exclusions);
// 通過 Condition 過濾掉不符合條件的類,減少后續(xù)的操作双炕,加快啟動速度
configurations = filter(configurations, autoConfigurationMetadata);
// 把符合條件和不符合條件的 config 放入 ConditionEvaluationReport
// 題外話:ConditionEvaluationReport 會在 ConditionEvaluationReportLoggingListener#ConditionEvaluationReportListener 監(jiān)聽器中被觸發(fā)
// ConditionEvaluationReportLoggingListener 的初始化在 SpringApplication 構(gòu)造方法中
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
// SpringFactoriesLoader 加載 AutoConfigurationImportFilter 的實現(xiàn)狞悲,一般至少有三個,分別是
// OnClassCondition妇斤,
// OnWebApplicationCondition摇锋,
// OnBeanCondition
// 使用 Condition 過濾,這里詳細(xì)的不說的站超,有興趣的朋友自己看下吧荸恕。
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);
}
Conclusion
- 通過以上,我們也可以定義自己的 AutoConfiguration死相,步驟也很簡單融求,只需要在 spring.factories 文件中如下定義:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
如果你還想配置條件的話,可以在 spring-autoconfigure-metadata.properties 文件中配置如下:
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration.ConditionalOnClass=org.springframework.security.crypto.encrypt.TextEncryptor
ConditionalOnClass 及以后的內(nèi)可以根據(jù)需要替換
- 關(guān)閉 AutoConfiguration :只要在系統(tǒng)屬性中配置 spring.boot.enableautoconfiguration=false 就可以了