当前位置: 首页 > 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.lryc.cn/news/599884.html

相关文章:

  • 1 51单片机-C51语法
  • java面试题(一)
  • 函数-变量的作用域和生命周期
  • 算法思维进阶 力扣 62.不同路径 暴力搜索 记忆化搜索 DFS 动态规划 C++详细算法解析 每日一题
  • Vue基础(24)_VueCompinent构造函数、Vue实例对象与组件实例对象
  • 【循环语句,求100内能被6整除的和】
  • 智能制造——解读39页MOM数字化工厂平台解决方案【附全文阅读】
  • Android 10.0 sts CtsSecurityBulletinHostTestCases的相关异常分析
  • ARPG开发流程第一章——方法合集
  • 负载均衡:提升业务性能的关键技术
  • 后端项目中大量 SQL 执行的性能优化
  • ptmalloc(glibc-2.12.1)源码解析2
  • 基于米尔瑞芯微RK3576开发板部署运行TinyMaix:超轻量级推理框架
  • Shopify Section Rendering API
  • 小白如何认识并处理Java异常?
  • 【嵌入式汇编基础】-ARM架构基础(二)
  • 从0到1:初创企业适合做企业架构吗?TOGAF 能带来什么?
  • 小架构step系列25:错误码
  • Haproxy七层代理及配置
  • 数据结构2-集合类ArrayList与洗牌算法
  • 在Word和WPS文字中添加的拼音放到文字右边
  • JS与Go:编程语言双星的碰撞与共生
  • 初识opencv04——图像预处理3
  • ModelWhale+数据分析 消费者行为数据分析实战
  • 判断子序列-leetcode
  • 广州 VR 安全用电技术:工作原理、特性及优势探析​
  • CTF-Web题解:“require_once(‘flag.php‘); assert(“$i == $u“);”
  • Linux系统基本配置以及认识文件作用
  • 双非上岸985!专业课140分经验!信号与系统考研专业课140+上岸中南大学,通信考研小马哥
  • 20分钟学会TypeScript