SpringBoot版本: 3.2.0
什么是自动装配
装配的是什么?
装配的就是 bean
, 也就是可以将一个 对象交给 Spring
容器管理
为什么可以自动?
SpringBoot
秉持的就是 约定优于配置,所以要想做到就得有一定的规则,比如
- 总得要告诉
Spring
我们想要装配的是什么类,通过什么方式告诉呢? 就是将想要注入的类集中写在一个固定位置的配置文件中- 在 2.7.0 之前使用的配置文件名称是
spring.factories
, 这个文件的位置是META-INF/spring.factories
- 在 2.7.0 之后使用的配置文件名称是
org.springframework.boot.autoconfigure.AutoConfiguration.imports
, 这个文件的位置是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
- 在 2.7.0 之前使用的配置文件名称是
- 可以认为配置文件是开发者需要提供的,当然
SpringBoot
内部也使用了这种机制,既然提供了配置文件,那么Spring
框架就需要按照约定的位置去找这些类了,这个在SpringBoot
中是通过@Import
注解导入了一个DeferredImportSelector
实现类来实现的,这个实现类是AutoConfigurationImportSelector
- 在
SpringBoot
中还提供了一种机制,就是通过一系列的条件判断我们在配置文件中写的这些类是否真的可以导入,这一系列条件是通过@ConditionalOnxxxx
注解来实现
接下来简单总结一下 SpringBoot3
的自动装配原理
- 通过
@Import
导入AutoConfigurationImportSelector
类, 可以参考 {% series Spring中Import注解源码解析 %} AutoConfigurationImportSelector
会去找所有classpath
下所有jar
下的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件,将文件里面的全限定类名加载进来- 对加载加来的类进行一系列的条件判断,符合条件的才会真的进行加载到
Spring
容器中
自动装配的示例
SpringBoot3自动装配案例 - 掘金 (juejin.cn)
SpringBoot3 自动装配的流程
SpringBoot
项目启动主类上只有一个 SpringBootApplication
注解,我们看一下这个注解的体系
涉及到自动注入的注解使用绿色标记出来了,所以最终需要看的就是 AutoConfigurationImportSelector
, 而这个类是一个 DeferredImportSelector
接口的实现类,具体参考 [[Spring中DeferredImportSelector源码解析]], 这里就直接给出 DeferredImportSelector
接口的执行流程
先看一下 AutoConfigurationImportSelector
这个类中相关的代码(只展示主流程部分)
java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// 根据这个方法可以知道当前 DeferredImportSelector 实现类对应的 Group 类是 AutoConfigurationGroup
// 步骤1: 根据这个方法得到具体的 Group 实现类
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
// 步骤4: 这个方法是从 Group 实现类中的 process() 方法调用过来的
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 步骤5: getCandidateConfigurations 方法会从配置文件加载类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
// 获取手动排除掉的类,比如我们配置了 exclude 相关的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 步骤6: 这里就是根据条件判断,过滤掉不符合条件的类
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
// 步骤2: 调用 Group 实现类中的 process() 方法
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 步骤3: 调用 AutoConfigurationImportSelector 类的 getAutoConfigurationEntry() 方法
// 所以这里并没有调用 AutoConfigurationImportSelector 类的 selectImports() 方法
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}
}
步骤1 和 步骤2 的调用在讲解 DeferredImportSelector
接口的时候已经讲过了,这里简单贴一下调用过程
csharp
AbstractApplicationContext#refresh
AbstractApplicationContext#invokeBeanFactoryPostProcessors
public PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
private PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#processConfigBeanDefinitions
ConfigurationClassParser#parse
for 循环 start[解析单个类]
ConfigurationClassParser#parse(AnnotationMetadata, java.lang.String)
ConfigurationClassParser#processImports (1)
for 循环结束
DeferredImportSelectorHandler#process (2)
DeferredImportSelectorGroupingHandler#processGroupImports
- 在
(1)
位置会找到DeferredImportSelector
类型的类,然后缓存起来,这个是在ConfigurationClassParser
的processImports()
方法中处理的 - 所以自定义的类都解析成
BeanDefiniton
之后(上面流程中的for
循环之后),开始执行DeferredImportSelectorHandler
类的process()
方法,这是因为(1)
中缓存的DeferredImportSelector
就是存放在DeferredImportSelectorHandler
类中,也就是在(2)
中调用到Group
类的process()
方法
接下来直接从 步骤3 开始看,
java
// AutoConfigurationGroup类
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 步骤3
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
}
// AutoConfigurationImportSelector类
// 步骤4: 这个方法是从 Group 实现类中的 process() 方法调用过来的
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 步骤5: getCandidateConfigurations 方法会从配置文件加载类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
// 获取手动排除掉的类,比如我们配置了 exclude 相关的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 步骤6: 这里就是根据条件判断,过滤掉不符合条件的类
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
步骤5 完成的事情--从配置文件获取数据,得到的是配置的全限定类名
java
// AutoConfigurationImportSelector 类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
return configurations;
}
// ImportCandidates 类
private static final String LOCATION = "META-INF/spring/%s.imports";
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// location = META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
String location = String.format(LOCATION, annotation.getName());
// 通过 ClassLoader 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
// 文件, 这个不是某个 jar 包,而是 classpath 下的所有jar 包对应路径的文件
// 所以返回的是 url 的集合
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 遍历每个 url, 读取对应的文件内容, 然后将文件内容中的内容添加到 importCandidates 中
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
下面截取 spring-boot-autoconfigure
这个包下 org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件的一部分内容看看
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
......
就是一行一个全限定类名而已,和 SpringBoot2.7
之前的 spring.factories
文件是不同的,这里也拿一个 spring.factories
文件内容对比一下
ini
# 这是一个 key
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
# 这是自动装配的 key
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
......
步骤6 完成的事情,对 步骤5中获取的全限定类名进行过滤
java
// AutoConfigurationImportSelector类
// 步骤4: 这个方法是从 Group 实现类中的 process() 方法调用过来的
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 步骤5: getCandidateConfigurations 方法会从配置文件加载类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
// 获取手动排除掉的类,比如我们配置了 exclude 相关的配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 步骤6: 这里就是根据条件判断,过滤掉不符合条件的类
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
- 调用
getConfigurationClassFilter()
方法获取classFilter
, 这个不跟下去了,直接说结论,其实也是根据spi
机制从spring.factories
文件中获取的,从上面列出的spring.factories
文件中可以看到,这个文件是key=values
的形式,也就是一个key
可以配置多个值,而获取classFilter
的key
是AutoConfigurationImportFilter
, 再看看spring.factories
文件中关于AutoConfigurationImportFilter
的配置
ini
# 这个配置文件也是在 spring-boot-autoconfigure 包下面
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
getConfigurationClassFilter()
方法的返回值是ConfigurationClassFilter
, 看看这个类,具体的过滤逻辑这里就不再分析
java
private static class ConfigurationClassFilter {
private final AutoConfigurationMetadata autoConfigurationMetadata;
// 上面讲到会从配置文件中加载到三个 AutoConfigurationImportFilter 实现类,会保存在这里然后调用过滤方法
private final List<AutoConfigurationImportFilter> filters;
}
解析文件和过滤后的处理
配置文件也解析了,过滤也过滤完了,回过头再看看 Group
类的 process()
方法是从哪里调用过来的,前面有,这里再贴一下流程
csharp
AbstractApplicationContext#refresh
AbstractApplicationContext#invokeBeanFactoryPostProcessors
public PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
private PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#processConfigBeanDefinitions
ConfigurationClassParser#parse
for 循环 start[解析单个类]
ConfigurationClassParser#parse(AnnotationMetadata, java.lang.String)
ConfigurationClassParser#processImports
for 循环结束
DeferredImportSelectorHandler#process
DeferredImportSelectorGroupingHandler#processGroupImports
所以是在 DeferredImportSelectorHandler 的类 process()
方法中处理的
java
// ConfigurationClassParser
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 就是在 getImport() 方法中调用到 Group 实现类的 process() 方法
// 返回值 Group 实现类的 selectImports 方法
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
// 拿到结果之后调用的又是 processImports方法, 这个就是和
// @Import 导入的是一个普通类的流程一致的
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
});
}
}
// ConfigurationClassParser
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 就是在这里调用到 Group 实现类的 process() 方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
从上面代码看到,拿到解析和结果的结果之后,通过 foreach
调用 processImports()
方法,这个方法就是处理单个 @Import
导入的类的方法,至此整个流程就串起来了