Spring Boot 中的 @EnableAutoConfiguration 是如何处理的?
点击上方蓝色“程序猿DD”,选择“设为星标”
回复“资源”获取独家整理的学习资料!
作者 | 温安适
引言
工作中,我们直接或间接的,用到@EnableAutoConfiguration注解。
今天,我们就聊聊@EnableAutoConfiguration的处理逻辑。
找核心方法
@Enable开头的的注解,上一般都有@Import用于指定要注解的逻辑实现类。
@EnableAutoConfiguration上的@Import,导入的是 AutoConfigurationImportSelector。
AutoConfigurationImportSelector就是要找的入口类。 类关系如下:
Aware系列都是用于注入响应的资源,Ordered用于排序。
值得关注的是 DeferredImportSelector,查看其类注释,简要翻译如下:
importselector的变体,在所有@Configuration bean之后运行,可以实现Ordered进行排序。
提供{getImportGroup(),它可以跨不同的选择器提供额外的排序和过滤逻辑。
DeferredImportSelector保证在所有@Configuration加载之后执行,也就说,如果有相关配置类已加载,则可以跳过自动装配类。
DeferredImportSelector是如何保证在@Configuration bean加载之后执行的呢???
带着这个疑问,我查看了ConfigurationClassPostProcessor#processConfigBeanDefinitions
(至于为什么要查看这个方法,请看上一篇 @Enable驱动原理)
浏览过程如下:
概要逻辑如下:
\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();
}
}
}
这里需要关注的是this.group.process,this.group.selectImports2个方法。
也就是AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我们需要关注的核心方法。
逐个分析
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()根据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 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)的实现如下
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删除重复的配置项 getExclusions 获取排除的组件 spring.autoconfigure.exclude,exclude,excludeName对应的值存储到set中 checkExcludedClasses 当前类在classLoader中,但是不在候选列表中内抛出异常 configurations.removeAll(exclusions)移除需要排除的配置项 filter 过滤不满足条件的自动装配组件 fireAutoConfigurationImportEvents 发送@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资源内容,合并一个key为全类名,Value为实现类名列表的Map,从Map中找到指定的key对应的实现类全类名列表
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 < match.length; 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 < 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 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 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;
}
}
排序规则如下:
按照字母顺序加载,否则先按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的逻辑处理类。
总结
@EnableAutoConfiguration的核心处理类是AutoConfigurationImportSelector AutoConfigurationImportSelector实现了DeferredImportSelector ImportSelector的解析在ConfigurationClassParser#processImports中处理在其中this.deferredImportSelectorHandler.handle(..)j将DeferredImportSelector放入队列,延后处理。DeferredImportSelector处理逻辑在ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。 AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我们需要关注的核心方法。 ConfigurationClassParser#doProcessConfigurationClass中的!this.conditionEvaluator.shouldSkip(...) 结合了各种Condition注解,实现了自定义配置覆盖自动装配。
往期推荐
扫一扫,关注我
一起学习,一起进步