当前位置: 首页 > news >正文

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 步:

自动配置类加载主要涉及三个类:AutoConfigurationImportSelectorSpringFactoriesLoaderConfigurationClassBeanDefinitionReader

  • AutoConfigurationImportSelectorSpringFactoriesLoader:这两个类主要用来加载第三方自动装配配置类的全类名
  • ConfigurationClassBeanDefinitionReader:主要将获取到的全类名加载成 bean

🥇 1. 启动入口加载配置类

SpringApplication.run(App.class, args);
  • 启动 Spring 容器
  • 自动注册 ConfigurationClassPostProcessor(处理配置类的后置处理器)

🥈 2. @EnableAutoConfiguration 被解析,回调其 AutoConfigurationImportSelector类selectImports() 方法

✅ 加载第三方自动装配配置类的全类名背后逻辑
  • @EnableAutoConfiguration 内部通过 @Import(AutoConfigurationImportSelector.class) 导入配置类选择器
  • 在配置类解析阶段,Spring 会回调其 AutoConfigurationImportSelector的selectImports() 方法。
  • 依赖AutoConfigurationImportSelectorSpringFactoriesLoader:这两个类来加载第三方自动装配配置类的全类名
🔎 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️⃣判断是否跳过配置类(@ConditionalshouldSkip(configClass)
2️⃣注册被 @Import 导入的配置类registerBeanDefinitionForImportedConfigurationClass(configClass)
3️⃣注册配置类中声明的 @Bean 方法loadBeanDefinitionsForBeanMethod(beanMethod)
4️⃣注册 XML 等外部资源loadBeanDefinitionsFromImportedResources()
5️⃣注册通过 ImportBeanDefinitionRegistrar 动态生成的 BeanloadBeanDefinitionsFromRegistrars()

🧪 5. 容器初始化,实例化 Bean

容器刷新阶段:

  • 根据已注册的 BeanDefinition 实例化 Bean
  • 自动配置类也就被激活(如:数据源、Redis、WebMvc 等)

📌 小结

阶段核心方法作用
启动SpringApplication.run()启动容器
自动装配发现selectImports()获取自动配置类名
解析配置类getAutoConfigurationEntry()去重、过滤、排除
加载配置来源loadFactoryNames() / loadSpringFactories()从 spring.factories 加载类路径
注册与初始化loadBeanDefinitions()注册 @Bean 到 BeanFactory

🎉 总结一句话:

Spring Boot 自动装配的核心就是:@EnableAutoConfiguration + SpringFactoriesLoader 读取配置类名并注册为 Bean!

http://www.dtcms.com/a/297127.html

相关文章:

  • 国产DevOps平台Gitee:如何重塑中国企业研发效能新格局
  • Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧
  • react中 多个层级 组件数据同用 组件之间传值 usecontext useReducer
  • Gitee如何成为国内企业DevOps转型的首选平台?
  • 璞致 PZSDR-P101:ZYNQ7100+AD9361 架构软件无线电平台,重塑宽频信号处理范式
  • ERNIE-4.5-0.3B 实战指南:文心一言 4.5 开源模型的轻量化部署与效能跃升
  • 规则分配脚本
  • 初识JVM--从Java文件到机器指令
  • 中国开源Qwen3 Coder与Kimi K2哪个最适合编程
  • “磁”力全开:钕铁硼重塑现代科技生活
  • Linux 网络与 Vim 编辑器操作
  • 3D实景的概念、特点及应用场景
  • 从“人工眼”到‘智能眼’:EZ-Vision视觉系统如何重构生产线视觉检测精度?
  • AI与区块链融合:2025年的技术革命与投资机遇
  • C++与Hive、Spark、libhdfs、ACID交互技巧
  • Vue2下
  • VR 技术在污水处理领域的创新性应用探索​
  • C++ string:准 STL Container
  • 【03】C#入门到精通——C# 输出格式、内容拼接、if判断 、bool 表达式、函数封装调用
  • 【深度学习优化算法】09:Adadelta算法
  • MyBatis-Plus中使用BaseMapper实现基本CRUD
  • MinIO:云原生对象存储的终极指南
  • Qt 与 SQLite 嵌入式数据库开发
  • 云原生可观测-日志观测(Loki)最佳实践
  • SQLite中SQL的解析执行:Lemon与VDBE的作用解析
  • mac下 vscode 运行 c++无法弹出窗口
  • 云原生介绍
  • 云原生 —— K8s 容器编排系统
  • FunASR实时多人对话语音识别、分析、端点检测
  • SQLite Insert 语句详解