原理简单概述
- springboot 各种starter简单来说就是为了加载各种依赖包中配置类@Configuration,这些配置类必须在模块的resources/META-INF 目录下spring.factories文件中键为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值中声明
- 因为我们不可能在启动类中声明扫描的包包含所有依赖包的配置类@Configuration,那样不现实。所以spring就提供了一种机制来加载这些配置类@Configuration。只要能够读取到这些配置类,那么就和平常自己在项目中声明的配置类@Configuration使用一样的方式来解析
总结:因为不想声明各种依赖包中配置类@Configuration对应的扫描的包,因为太多太乱,又想让spring管理这些配置类@Configuration,所以使用了其它方式可以读取这些配置类。
源码解析
(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
在spring @Import介绍这篇文章中已经说明了@Import的用法以及AutoConfigurationImportSelector属于DeferredImportSelector(延期的ImportSelector)类型。下面的说明全部基于此。
对于DeferredImportSelector,会在spring处理完我们自定义的@Configuration bean 后运行(为啥呢?因为自动配置类中大都会有@Condition,就比如用户若是已经定义了某个bean,那么自动配置的就不生效)。这里需要对ConfigurationClassPostProcessor类作为BeanFactoryPostProcessor都做了哪些事情要有了解,这里不做介绍。可以看我之前ConfigurationClassPostProcessor这篇文章。这里只看具体代码。
ConfigurationClassParser#parse简化后代码,最后一行代码就是处理DeferredImportSelector。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
this.deferredImportSelectorHandler.process();
}
在spring @Import介绍这篇文章中已经说了对于 DeferredImportSelector,之前就是被包装成DeferredImportSelectorHolder然后放到deferredImportSelectorHandler的一个list集合中,这里就是直接解析deferredImportSelectorHandler的这个集合就可以了。
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<>();
}
}
这里就是声明了一个DeferredImportSelectorGroupingHandler,然后循环调用其register方法处理这些DeferredImportSelectorHolder。这里先看register方法。
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
//1
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
- 获取DeferredImportSelectorHolder 中的ImportSelector所属的组。下面是AutoConfigurationImportSelector获取组源码。
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
- 从grouping属性中获取对应的DeferredImportSelectorGrouping ,没有则创建,然后将该DeferredImportSelectorHolder 添加到该组中。注意这里的createGroup方法,后面会用到该方法返给DeferredImportSelectorGrouping 这个参数。简单理解就是spring把DeferredImportSelector分了组。这里把同一组的收集了起来。
- 最后一行代码将注解属性和ConfigurationClass映射,需要结合后续处理一起看(1处代码)
register完了之后才是真正的处理handler.processGroupImports()
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
});
}
}
可以看到这里就是按照register方法分组后来处理的。 grouping.getImports()源码如下
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
- 使用group成员变量循环处理DeferredImportSelectorHolder 。上面说了AutoConfigurationImportSelector的group为AutoConfigurationGroup,所以上述register方法中createGroup会创建AutoConfigurationGroup的实例,也就是这里的group成员变量的值。
AutoConfigurationGroup#process源码
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
//AutoConfigurationEntry就包含那些自动配置类的全类名,当然还包含排除的类。
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
获取AutoConfigurationEntry会调用AutoConfigurationImportSelector#getAutoConfigurationEntry方法,该方法后续会有下面的调用
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
是不是很熟悉了。看到了EnableAutoConfiguration.class和SpringFactoriesLoader.loadFactoryNames,对于SpringFactoriesLoader.loadFactoryNames不熟悉的可以看其它文章或者我的这篇文章springboot2.1.7启动分析(一)SpringApplication实例化,这里就不在说明。
接着看grouping.getImports()源码最后一行代码:return this.group.selectImports()部分源码
public Iterable<Entry> selectImports() {
...
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
简单来说就是把配置类包装成Entry返回.
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
Entry中的metadata就是上面register方法最后一行代码configurationClassesd(map类型)中键是一致的,所以这一获取到的ConfigurationClass 都是一样的。都是DeferredImportSelectorHolder 中的ConfigurationClass 。
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
});
processImports方法在spring @Import介绍中已经说明过,该方法的第三个参数就是候选的@import对应的value值。这里正是把entry.getImportClassName()也就是配置类包装成该参数。这里相当于又是对于@import值的处理,因为这些配置类应该都不是ImportSelector或ImportBeanDefinitionRegistrar类型,将会作为@Configuration类处理。
总结
这里只是简单的说明了spring是何时以及怎么读取这些自动配置类,以及将其作为@Configuration类处理,中间省略了很多细节,这篇文章只是关注那些生效的自动配置类是如何被spring加载的。