在SpringBoot中,EnableAutoConfiguration註解用於開啟自動裝配功能。 本文將詳細分析該註解的工作流程。 # EnableAutoConfiguration註解 啟用SpringBoot自動裝配功能,嘗試猜測和配置可能需要的組件Bean。 自動裝配類通常是根據類路徑和定義 ...
在SpringBoot中,EnableAutoConfiguration註解用於開啟自動裝配功能。
本文將詳細分析該註解的工作流程。
EnableAutoConfiguration註解
啟用SpringBoot自動裝配功能,嘗試猜測和配置可能需要的組件Bean。
自動裝配類通常是根據類路徑和定義的Bean來應用的。例如,如果類路徑上有tomcat-embedded.jar,那麼可能需要一個TomcatServletWebServerFactory(除非已經定義了自己的Servlet WebServerFactory Bean)。
自動裝配試圖儘可能地智能化,並將隨著開發者定義自己的配置而取消自動裝配相衝突的配置。開發者可以使用exclude()排除不想使用的配置,也可以通過spring.autoconfig.exclude屬性排除這些配置。自動裝配總是在用戶定義的Bean註冊之後應用。
用@EnableAutoConfiguration註解標註的類所在包具有特定的意義,通常用作預設掃描的包。通常建議將@EnableAutoConfiguration(如果沒有使用@SpringBootApplication註解)放在根包中,以便可以搜索所有子包和類。
自動裝配類是普通的Spring @Configuration類,使用SpringFactoriesLoader機制定位。通常使用@Conditional方式裝配,最常用的是@ConditionalOnClass和@ConditionalOnMissingBean註解。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* 當類路徑下沒有指定的類時,可以使用這個屬性指定排除的類
*/
String[] excludeName() default {};
}
該註解Import了AutoConfigurationImportSelector類,AutoConfigurationImportSelector類實現了DeferredImportSelector介面。
Import註解和DeferredImportSelector介面在之前的"Spring @Import註解源碼分析"中詳細分析過,此處在介紹它們,只分析AutoConfigurationImportSelector的工作流程。
AutoConfigurationImportSelector類
DeferredImportSelector介面
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.
Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.
Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
AutoConfigurationGroup類
AutoConfigurationImportSelector的getImportGroup方法返回了AutoConfigurationGroup類。
private static class AutoConfigurationGroup implements
DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
// ... 略
@Override
public void process(
AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
// AutoConfigurationEntry類使用List保存Configuration類
AutoConfigurationEntry autoConfigurationEntry =
((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
@Override
public Iterable<Entry> selectImports() {
// 查找排除的配置類
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());
}
// ... 略
}
從上面的代碼可以看出,查找自動裝配類的邏輯在getAutoConfigurationEntry方法中。
getAutoConfigurationEntry方法
從META-INF/spring.factories文件解析EnableAutoConfiguration配置。
META-INF/spring.factories文件示例:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
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);
// 這裡的Filter是從META-INF/spring.factories文件解析出來的
configurations = getConfigurationClassFilter().filter(configurations);
// 觸發事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 從META-INF/spring.factories文件查找EnableAutoConfiguration配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
SpringFactoriesLoader類loadFactoryNames方法
Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.
public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
// 從類路徑下查找META-INF/spring.factories文件
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 properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName :
StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 把配置添加緩存
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}