Spring Boot 自动装配底层源码实现详解
🌟 Spring Boot 自动装配底层源码实现详解
✅ 本文深入源码,完整剖析 Spring Boot 自动装配背后的实现逻辑,适合有一定基础的开发者查漏补缺、面试复习。
✅ 总结成流程图(详细调用链)
@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. 启动入口加载配置类
SpringApplication.run(App.class, args);
- 启动 Spring 容器
- 自动注册
ConfigurationClassPostProcessor
(处理配置类的后置处理器)
🥈 2. @EnableAutoConfiguration
被解析,回调其 AutoConfigurationImportSelector类selectImports()
方法
✅ 加载第三方自动装配配置类的全类名背后逻辑
@EnableAutoConfiguration
内部通过@Import(AutoConfigurationImportSelector.class)
导入配置类选择器- 在配置类解析阶段,Spring 会回调其 AutoConfigurationImportSelector的
selectImports()
方法。 - 依赖
AutoConfigurationImportSelector
、SpringFactoriesLoader
:这两个类来加载第三方自动装配配置类的全类名
🔎 2.1 AutoConfigurationImportSelector类解析
selectImports()
方法详解
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
结构:
protected static class AutoConfigurationEntry {private final List<String> configurations; // 要注入的配置类private final Set<String> exclusions; // 要排除的类
}
getAutoConfigurationEntry()
方法详解
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()
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()
方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = (classLoader != null) ? classLoader : SpringFactoriesLoader.class.getClassLoader();// factoryTypeName :org.springframework.boot.autoconfigure.EnableAutoConfigurationString factoryTypeName = factoryType.getName();// 根据key返回对应的第三方配置类的全路径名,其中return (List) loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
🔧 loadSpringFactories()
方法
// 加载 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()
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()
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 方法对应的 BeanDefinitionfor (BeanMethod beanMethod : configClass.getBeanMethods()) {this.loadBeanDefinitionsForBeanMethod(beanMethod);}// 4️⃣ 注册 @ImportResource 引用的外部配置文件(如 XML)this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 5️⃣ 注册通过 ImportBeanDefinitionRegistrar 动态注册的 Beanthis.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!