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.EnableAutoConfiguration String 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>
缓存并返回 - 自动装配类的注册入口:key 是接口,value 是其实现类路径
🏅 3. 返回的配置类名 → 注册为配置类
- 上一步中
selectImports()
返回的是List
类型的配置类路径 - 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 方法对应的 BeanDefinition for (BeanMethod beanMethod : configClass.getBeanMethods()) { this.loadBeanDefinitionsForBeanMethod(beanMethod); } // 4️⃣ 注册 @ImportResource 引用的外部配置文件(如 XML) this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 5️⃣ 注册通过 ImportBeanDefinitionRegistrar 动态注册的 Bean this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }}
✅ 小结:整个方法做了 5 件事
@Conditional
)shouldSkip(configClass)
@Import
导入的配置类registerBeanDefinitionForImportedConfigurationClass(configClass)
@Bean
方法loadBeanDefinitionsForBeanMethod(beanMethod)
loadBeanDefinitionsFromImportedResources()
ImportBeanDefinitionRegistrar
动态生成的 BeanloadBeanDefinitionsFromRegistrars()
🧪 5. 容器初始化,实例化 Bean
容器刷新阶段:
- 根据已注册的
BeanDefinition
实例化 Bean - 自动配置类也就被激活(如:数据源、Redis、WebMvc 等)
📌 小结
SpringApplication.run()
selectImports()
getAutoConfigurationEntry()
loadFactoryNames()
/ loadSpringFactories()
loadBeanDefinitions()
🎉 总结一句话:
Spring Boot 自动装配的核心就是:@EnableAutoConfiguration + SpringFactoriesLoader 读取配置类名并注册为 Bean!