目錄
- 自動配置 @EnableAutoConfiguration
- 開啟自動配置
- 讀取配置
- 提前過濾
- 自動配置配置包 @AutoConfigurationPackage
自動配置 @EnableAutoConfiguration
開啟自動配置
在Spring 啟動類上的 @SpringBootApplication 中有 @EnableAutoConfiguration
讀取配置
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
也就是導入了 AutoConfigurationImportSelector
我們在 Spring 時代知道這個類會返回配置類的全類名
也就是在 selectImports 方法中返回了自動配置的相關類的全類名的集合
Spring boot 自動裝配的配置類有144個
如果一個一個寫,那代碼就太長了
所以Spring 就把這些配置類都配置在一個文件中
但是如果你debug到 selectImports() 里會發現并沒有進這個方法
是因為 DeferredImportSelector 這個接口 他其實是進入的 getImportGroup()
public Class<? extends Group> getImportGroup() {return AutoConfigurationGroup.class;
}最終調用的是 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process//最終還是會調用 deferredImportSelector
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).deferredImportSelector(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);
} //后續排序后返回
//排序規則:
1. 按 @AutoConfigureOrder 配置的順序
2. 按 @AutoConfigureAfter 和 @AutoConfigureBefore排
getAutoConfigurationEntry(annotationMetadata)://讀取 @EnableAutoConfiguration 的配置信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//讀取所有的 META-INF/spring.factories 文件中配置的配置類
//讀取所有的 @AutoConfiguration 注解的配置類
//這里會把 classLoader里所有的META-INF/spring.factories文件都讀出來匯總
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//有序去重 這一步很好玩 平時也可以用
//new ArrayList<>(new LinkedHashSet<>(list));
configurations = removeDuplicates(configurations);//讀取 注解中配置的排除類 排除掉
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);//這一步 是提前過濾一些肯定不能行的配置類
//具體怎么做的下邊分析
configurations = getConfigurationClassFilter().filter(configurations);
//觸發自動配置導入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
META-INF/spring.factories 文件示例:
//文件內容 接口 = 類名列表,多個用“,”隔開文件解析方法 loadSpringFactories()://讀取classLoader中所有的文件
Map<String, List<String>> result = new HashMap<>();
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {URL url = urls.nextElement();//讀取并創建資源UrlResource resource = new UrlResource(url);//解析成 properties 其實就是個 MapProperties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();// 按 ‘,’ 分割String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {//這個就是常用的 map的value是listresult.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}
}
return result;
提前過濾
getConfigurationClassFilter().filter(configurations):getConfigurationClassFilter():
//讀取到三個
//OnBeanCondition
//OnClassCondition
//OnWebApplicationCondition
filters = SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
for (AutoConfigurationImportFilter filter : filters) {invokeAwareMethods(filter);
}
//這里再創建 ConfigurationClassFilter 的時候就讀取了預處理的配置文件
//META-INF/spring-autoconfigure-metadata.properties
//這個配置類里邊已經配好類名和提前過濾配置的內容
return this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);//根據配置類中的內容提前過濾一下
//OnBeanCondition :沒有配置類型的Bean定義就別解析了
//OnClassCondition :沒有配置的類文件就別解析了
//OnWebApplicationCondition :不是配置的上下文類型就別解析了
//這樣一來就快速的過濾掉一些肯定不能創建的配置類
ConfigurationClassFilter.filter(configurations):
META-INF/spring-autoconfigure-metadata.properties 文件內容示例:
自動配置配置包 @AutoConfigurationPackage
這個注解是配置自動裝配所作用的包的對象
在實際工作中有的時候需要把一個老項目代碼遷移到新的項目中
為了避免出現大量改動可以把包平移到新的項目中
然后在新項目中配置掃描老項目的包名
有的時候會發現Bean注冊沒問題,但是一些自動裝配掃描的Bean就沒有注冊
比如 ES 自動裝配的時候就會基于這個 package 來掃描