🌟 Spring Boot 自动装配底层源码实现详解
✅ 本文深入源码,完整剖析 Spring Boot 自动装配背后的实现逻辑,适合有一定基础的开发者查漏补缺、面试复习。
✅ 总结成流程图(详细调用链)
text
@SpringBootApplication
│
▼
@EnableAutoConfiguration ⬅️ 自动装配启动入口
│
▼
@Import(AutoConfigurationImportSelector.class) ⬅️ 导入自动配置选择器类
│
▼
SpringApplication.run()
│
▼
ApplicationContext.refresh() ⬅️ 启动容器
│
▼
invokeBeanFactoryPostProcessors() ⬅️ 调用所有 BeanFactory 后处理器
│
▼
ConfigurationClassPostProcessor ⬅️ 处理配置类
│
▼
parse() ⬅️ 解析配置类
│
▼
ConfigurationClassParser
│
├── scan() / processConfigurationClass()
│
└── processImports() ⬅️ 处理 @Import 注解
│
▼
AutoConfigurationImportSelector ⬅️ 自动装配选择器
│
▼
selectImports(annotationMetadata) ⬅️ 返回配置类路径数组
│
├── getAutoConfigurationEntry()
│ ├── getCandidateConfigurations()
│ │ ├── SpringFactoriesLoader.loadFactoryNames()
│ │ │ └── loadSpringFactories() ⬅️ 加载 spring.factories
│ │ └── ImportCandidates.load() ⬅️ 加载 .imports 文件
│ └── 去重、排除、过滤、事件发布等处理
│
▼
注册自动配置类为 @Configuration 配置类
│
▼
loadBeanDefinitions() ⬅️ 将 @Bean 方法注册为 BeanDefinition
│
▼
容器初始化,自动配置类生效
🧠 自动装配的核心流程分为 5 步:
自动配置类加载主要涉及三个类:AutoConfigurationImportSelector
、SpringFactoriesLoader
,ConfigurationClassBeanDefinitionReader
AutoConfigurationImportSelector
、SpringFactoriesLoader
:这两个类主要用来加载第三方自动装配配置类的全类名ConfigurationClassBeanDefinitionReader
:主要将获取到的全类名加载成 bean。
🥇 1. 启动入口加载配置类
java
SpringApplication.run(App.class, args);
- 启动 Spring 容器
- 自动注册
ConfigurationClassPostProcessor
(处理配置类的后置处理器)
🥈 2. @EnableAutoConfiguration
被解析,回调其 AutoConfigurationImportSelector类selectImports()
方法
✅ 加载第三方自动装配配置类的全类名背后逻辑
@EnableAutoConfiguration
内部通过@Import(AutoConfigurationImportSelector.class)
导入配置类选择器- 在配置类解析阶段,Spring 会回调其 AutoConfigurationImportSelector的
selectImports()
方法。 - 依赖
AutoConfigurationImportSelector
、SpringFactoriesLoader
:这两个类来加载第三方自动装配配置类的全类名
🔎 2.1 AutoConfigurationImportSelector类解析
selectImports()
方法详解
java
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry entry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(entry.getConfigurations());
}
}
✅ 作用 :调用 getAutoConfigurationEntry
获取配置类路径,然后返回给 Spring 进行后续解析。
📦 其中AutoConfigurationEntry
结构:
java
protected static class AutoConfigurationEntry {
private final List<String> configurations; // 要注入的配置类
private final Set<String> exclusions; // 要排除的类
}
getAutoConfigurationEntry()
方法详解
java
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// ① 加载所有候选配置类路径
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// ② 去重
configurations = this.<String>removeDuplicates(configurations);
// ③ 获取排除的类(通过exclude属性或@EnableAutoConfiguration(exclude = ...)配置)
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// ④ 过滤无效配置类(比如条件不满足的 @ConditionalOnClass)
configurations = this.getConfigurationClassFilter().filter(configurations);
// ⑤ 发布事件通知监听器
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
✅ 作用总结:
- 负责真正收集所有需要自动装配的配置类
- 做了去重、排除、过滤、事件通知等处理
获取配置类路径 → getCandidateConfigurations()
java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
// **获取文件配置类**
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())
);
// Spring Boot 2.7+ 新增的支持 .imports 文件配置方式
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader())
.forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. " +
"If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
✅ 作用总结:
- 在configurations后继续追加
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
(为了适应2.7+后的配置文件)自动配置类的路径
📦 2.2 SpringFactoriesLoader类
源码解析
🔧 loadFactoryNames()
方法
java
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null) ? classLoader : SpringFactoriesLoader.class.getClassLoader();
// factoryTypeName :org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
// 根据key返回对应的第三方配置类的全路径名,其中
return (List) loadSpringFactories(classLoaderToUse)
.getOrDefault(factoryTypeName, Collections.emptyList());
}
🔧 loadSpringFactories()
方法
java
// 加载 META-INF/spring.factories文件成对应的map
// 其中: key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
// value:com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) return result;
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String key = ((String) entry.getKey()).trim();
String[] valueList = StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String value : valueList) {
result.computeIfAbsent(key, k -> new ArrayList<>()).add(value.trim());
}
}
}
// 去重、封装成不可变集合
result.replaceAll((factoryType, implementations) ->
implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))
);
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
throw new IllegalArgumentException(\"Unable to load factories from location [META-INF/spring.factories]\", ex);
}
}
✅ 作用总结:
- 从类路径中加载
META-INF/spring.factories
- 将其转为
Map<String, List<String>>
缓存并返回 - 自动装配类的注册入口:key 是接口,value 是其实现类路径
🏅 3. 返回的配置类名 → 注册为配置类
- 上一步中
selectImports()
返回的是List<String>
类型的配置类路径 - Spring 会将其视为
@Configuration
进行进一步解析
🏆 4. 配置类的 @Bean
方法 → 注册为 BeanDefinition
由 ConfigurationClassBeanDefinitionReader
完成:
- 注册配置类本身为 Bean
- 注册其
@Bean
方法为 loadBeanDefinitions - 最终注册到
DefaultListableBeanFactory
我们来逐步解读Bean注册 loadBeanDefinitions 源码方法,它们位于ConfigurationClassBeanDefinitionReader
类中,是 将解析后的配置类注册为 BeanDefinition 的核心流程。
🔍 方法一:loadBeanDefinitions()
java
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
✨ 作用:
这是入口方法,接收一批配置类(@Configuration
、@Import
导入的类等),并逐个调用内部的 loadBeanDefinitionsForConfigurationClass()
方法进行处理。
🧠 参数说明:
configurationModel
:Spring 通过ConfigurationClassParser
解析得到的完整配置类集合。TrackedConditionEvaluator
:用于判断配置类是否应被跳过(基于@Conditional
)。
🔍 方法二:loadBeanDefinitionsForConfigurationClass()
java
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
// 1️⃣ 如果该配置类不应被注册(如被 @Conditional 排除),则移除原有的 BeanDefinition(如果存在)
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
// 同时移除该类对应的 Import 注册信息
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
} else {
// 2️⃣ 注册配置类本身为 BeanDefinition(如果是被 @Import 导入的)
if (configClass.isImported()) {
this.registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 3️⃣ 注册 @Bean 方法对应的 BeanDefinition
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
this.loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 4️⃣ 注册 @ImportResource 引用的外部配置文件(如 XML)
this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 5️⃣ 注册通过 ImportBeanDefinitionRegistrar 动态注册的 Bean
this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
}
✅ 小结:整个方法做了 5 件事
步骤 | 作用 | 方法 |
---|---|---|
1️⃣ | 判断是否跳过配置类(@Conditional ) |
shouldSkip(configClass) |
2️⃣ | 注册被 @Import 导入的配置类 |
registerBeanDefinitionForImportedConfigurationClass(configClass) |
3️⃣ | 注册配置类中声明的 @Bean 方法 |
loadBeanDefinitionsForBeanMethod(beanMethod) |
4️⃣ | 注册 XML 等外部资源 | loadBeanDefinitionsFromImportedResources() |
5️⃣ | 注册通过 ImportBeanDefinitionRegistrar 动态生成的 Bean |
loadBeanDefinitionsFromRegistrars() |
🧪 5. 容器初始化,实例化 Bean
容器刷新阶段:
- 根据已注册的
BeanDefinition
实例化 Bean - 自动配置类也就被激活(如:数据源、Redis、WebMvc 等)
📌 小结
阶段 | 核心方法 | 作用 |
---|---|---|
启动 | SpringApplication.run() |
启动容器 |
自动装配发现 | selectImports() |
获取自动配置类名 |
解析配置类 | getAutoConfigurationEntry() |
去重、过滤、排除 |
加载配置来源 | loadFactoryNames() / loadSpringFactories() |
从 spring.factories 加载类路径 |
注册与初始化 | loadBeanDefinitions() |
注册 @Bean 到 BeanFactory |
🎉 总结一句话:
Spring Boot 自动装配的核心就是:@EnableAutoConfiguration + SpringFactoriesLoader 读取配置类名并注册为 Bean!