Spring Boot 自动配置全流程深度解析

        在 Spring Boot 的世界里,“约定优于配置” 理念通过自动配置机制展现得淋漓尽致。从一个简单的@SpringBootApplication注解开始,背后隐藏着一套精妙的自动配置加载流程。本文将从@SpringBootApplication出发,逐步拆解自动配置类是如何被发现、加载到 Spring 容器中的,结合关键源码,带你看透自动配置的本质。

一、@SpringBootApplication:自动配置的入口

注解拆解

@SpringBootApplication是一个复合注解,核心包含以下三个关键注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标记为Spring Boot配置类,本质是@Configuration
@EnableAutoConfiguration // 开启自动配置核心注解
@ComponentScan(excludeFilters = { // 组件扫描,默认扫描当前包及子包@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {//... 其他属性
}

其中,@EnableAutoConfiguration是触发自动配置的关键开关,它的功能实现依赖 @Import 导入的ImportSelector实现类,开启自动配置类的加载流程。

关于@Import注解的详细内容,可以看:

​​​​​​Spring 中 @Import 注解:Bean 注入的灵活利器-CSDN博客

二、@EnableAutoConfiguration:开启自动配置引擎

@EnableAutoConfiguration 的结构

@EnableAutoConfiguration注解定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage 
@Import(AutoConfigurationImportSelector.class) 
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

这里通过 @Import 引入 AutoConfigurationImportSelector ,这个类实现了 ImportSelector接口,负责收集并返回需要自动配置的类名集合,是自动配置类加载的核心桥梁。

三、AutoConfigurationImportSelector:筛选自动配置类

1)selectImports 方法:启动加载流程

AutoConfigurationImportSelector 实现了 ImportSelector接口,重写 selectImports方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) { return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); 
}

逻辑是:先判断自动配置是否启用(可通过spring.boot.enableautoconfiguration属性控制),若启用则调用getAutoConfigurationEntry获取自动配置入口,最终返回需要注入容器的配置类名数组。

2)getAutoConfigurationEntry:构建配置入口

getAutoConfigurationEntry方法是加载自动配置类的核心逻辑入口:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1. 获取注解属性AnnotationAttributes attributes = getAttributes(annotationMetadata); // 2. 获取候选配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3. 去重、排除处理// 去重configurations = removeDuplicates(configurations); // 排除指定配置Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 4. 条件过滤configurations = getConfigurationClassFilter().filter(configurations);//  5. 事件发布fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); 
}
1. 获取注解属性

AnnotationAttributes attributes = getAttributes(annotationMetadata);

通过 getAttributes 方法,从 @EnableAutoConfiguration 注解元数据(annotationMetadata)里,提取注解属性(像 exclude、excludeName 等配置 ),为后续筛选配置类提供依据,明确哪些配置类要排除。

2. 获取候选配置类

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

getCandidateConfigurations 方法是核心,它会去加载 META-INF/spring.factories 文件中,key 为 EnableAutoConfiguration 对应的配置类全限定名,把这些配置类收集成列表,作为自动配置的 “候选池” 。

3. 去重、排除处理

去重:

configurations = removeDuplicates(configurations);

调用 removeDuplicates,遍历 configurations 列表,剔除重复的配置类全限定名,保证配置类唯一。

排除指定配置

Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);

4. 条件过滤
  • getExclusions:从注解属性里解析出用户通过 exclude 等指定的、要排除的配置类名,存入 exclusions 集合。
  • checkExcludedClasses:校验排除的类是否合法(比如类是否存在等 )。
  • configurations.removeAll(exclusions):从候选配置类列表里,移除 exclusions 中指定的配置类,实现用户自定义排除。

configurations = getConfigurationClassFilter().filter(configurations);

getConfigurationClassFilter 获取条件过滤器(内部结合 @Conditional 系列注解,如 @ConditionalOnClass、@ConditionalOnBean 等逻辑 ),对 configurations 列表遍历筛选,只有满足所有 @Conditional 条件的配置类,才会保留,确保最终加载的配置类符合运行环境要求。

5. 事件发布

fireAutoConfigurationImportEvents(configurations, exclusions);

fireAutoConfigurationImportEvents 方法发布自动配置加载事件(如AutoConfigurationImportEvent ),让项目里的 ApplicationListener 监听器能感知到自动配置加载过程,便于做拓展(比如记录日志、额外初始化逻辑等 )。

3)getCandidateConfigurations:加载 spring.factories 配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}@Override
protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class; 
}

关键逻辑:

SpringFactoriesLoader.loadFactoryNames 会从所有 Jar 包的META-INF/spring.factories文件中,读取key为 EnableAutoConfiguration.class 对应的配置类列表。例如,Spring Boot 自身的自动配置、第三方 Starter(如 MyBatis - Starter)的自动配置,都通过这种方式 “注册” 到加载流程中。

四、SpringFactoriesLoader:实现配置类发现

SpringFactoriesLoader是 Spring 框架提供的工具类,核心方法loadFactoryNames逻辑简化如下:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); Map<String, List<String>> factories = new LinkedHashMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {factories.add(factoryTypeName, factoryImplementationName.trim()); }}}cache.put(classLoader, factories);return factories;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

其中:

  • FACTORIES_RESOURCE_LOCATION 常量值为"META-INF/spring.factories",指定了配置文件路径。
  • 方法会遍历类路径下所有 Jar 包的META-INF/spring.factories文件,将文件内容解析为Properties,再根据factoryType(这里是EnableAutoConfiguration)的全类名,提取对应的配置类名列表。

五、自动配置类的生效:@Conditional 的把关

加载到候选配置类后,Spring Boot 并非直接全部注入容器,而是通过 **@Conditional系列注解 ** 做 “条件校验”。常见条件注解:

  • @ConditionalOnClass:类路径下存在指定类时生效。
  • @ConditionalOnBean:容器中存在指定 Bean 时生效。
  • @ConditionalOnProperty:配置属性满足条件时生效(如spring.datasource.enabled=true )。

以DataSourceAutoConfiguration为例,简化代码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 
@ConditionalOnMissingBean(type = "javax.sql.DataSource") 
@EnableConfigurationProperties(DataSourceProperties.class) 
public class DataSourceAutoConfiguration {//... 配置数据源Bean逻辑
}

只有项目依赖了DataSource相关类、容器中没有自定义DataSource Bean 时,该自动配置类才会真正生效,向容器注入数据源相关 Bean。

六、总结:自动配置全流程脉络

@SpringBootApplication开始,自动配置的完整流程可梳理为:

1. 注解触发:@SpringBootApplication 包含 @EnableAutoConfiguration,通过 @Import引入AutoConfigurationImportSelector(ImportSelector接口的实现类)。

2. 收集配置类AutoConfigurationImportSelectorselectImports 方法调用getAutoConfigurationEntry,再通过 getCandidateConfigurations,借助SpringFactoriesLoader 读取所有 Jar 包META-INF/spring.factories 中EnableAutoConfiguration 对应的配置类。

3. 筛选生效类:结合 @Conditional 注解,过滤出满足条件的自动配置类,注入 Spring 容器,完成自动配置。

这套机制让 Spring Boot 能根据项目依赖 “智能” 配置环境,既降低了手动配置的复杂度,又通过@Conditional保证了灵活性,是 Spring Boot “开箱即用” 特性的核心支撑。理解这一流程,无论排查自动配置问题,还是自定义 Starter,都能做到心中有数 。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96502.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI:业务驱动与技术赋能:企业智能化应用的双向进化深度指南

一、业务与技术的双螺旋进化模型 1.1 从单向适配到双向驱动的认知转变 传统的信息化建设往往遵循"业务提需求、技术做实现"的线性模式&#xff0c;这种模式在稳定的业务环境中确实有效&#xff0c;但在当前快速变化的数字化时代已经显露出明显的局限性。真正的数字化…

2721. 【SDOI2010】外星千足虫

2721. 【SDOI2010】外星千足虫 题解 题目描述 题目描述 公元2089年6月4日&#xff0c;在经历了17年零3个月的漫长旅行后&#xff0c;“格纳格鲁一号”载人火箭返回舱终于安全着陆。此枚火箭由美国国家航空航天局&#xff08;NASA&#xff09;研制发射&#xff0c;行经火星、…

[RestGPT] RestGPT智能体

第3章&#xff1a;RestGPT智能体 欢迎回来&#x1f43b;‍❄️ 在第1章&#xff1a;配置与环境中&#xff0c;我们为RestGPT配备了必要的"钥匙和密码"&#xff1b;在第2章&#xff1a;OpenAPI规范(OAS)中&#xff0c;我们为它提供了与在线服务对话的"使用说明…

笔记本电脑Windows+Ubuntu 双系统,Ubuntu无法挂载Windows的硬盘 报错问题解决

目录 一、前情提要 二、解决方案步骤 第一步&#xff1a;进入Windows进行修复和检查。这是最关键的一步&#xff0c;目的是让Windows来检查和修复它自己的文件系统。 第二步&#xff1a;回到Ubuntu验证挂载 三、总结与预防 一、前情提要 网上找到许多解决方案&#xff0c…

加密货币与区块链:六大刑事重灾区

高鹏律师&#xff08;首席数据官&#xff09;数字经济团队创作&#xff0c;AI辅助在数字货币的世界里&#xff0c;一夜暴富的传说屡见不鲜&#xff0c;但顷刻间失去所有的悲剧也时有发生&#xff0c;现在&#xff0c;我将为您剖析加密货币与区块链领域的六大刑事风险重灾区&…

Spring Ai 1.0.1中存在的问题:使用MessageChatMemoryAdvisor导致System未被正确的放在首位

使用MessageChatMemoryAdvisor导致System未被正确的放在首位 如下是使用Spring Ai实现多轮对话的官方例子&#xff08;文档地址&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff09;&#xff1a;AutowiredChatMemoryRepository chatMemor…

全景式综述|多模态目标跟踪全面解析:方法、数据、挑战与未来

【导读】 目标跟踪&#xff08;Visual Object Tracking, VOT&#xff09;一直是计算机视觉领域的核心问题之一&#xff0c;广泛应用于自动驾驶、无人机监控、人机交互等场景。随着单模态方法在复杂环境下逐渐遇到瓶颈&#xff0c;多模态视觉目标跟踪&#xff08;Multi-Modal V…

怎么用pytorch训练一个模型,并跑起来

MNIST 手写数字识别 任务描述 MNIST 手写数字识别是机器学习和计算机视觉领域的经典任务&#xff0c;其本质是解决 “从手写数字图像中自动识别出对应的数字&#xff08;0-9&#xff09;” 的问题&#xff0c;属于单标签图像分类任务&#xff08;每张图像仅对应一个类别&#x…

Qt应用程序发布方式

解决的问题&#xff1a;在自己电脑上用QT Creator编译的exe文件放到其他电脑上不能正常打开的问题。1、拷贝已经编译好的exe应用程序到桌面文件夹。桌面新建文件夹WindowsTest&#xff0c;并且将编译好的软件WindowTest.exe放入此文件夹中。2、在此文件夹空白处按住Shift再点击…

Linux 软件编程(九)网络编程:IP、端口与 UDP 套接字

1. 学习目的实现 不同主机之间的进程间通信。在 Linux 下&#xff0c;进程间通信&#xff08;IPC&#xff09;不仅可以发生在同一台主机上&#xff0c;也可以通过网络实现不同主机之间的通信。要做到这一点&#xff0c;必须同时满足以下两个条件&#xff1a;物理层面&#xff1…

5.Kotlin作用于函数let、run、with、apply、also

选择建议 需要返回值&#xff1a;使用 let、run 或 with配置对象&#xff1a;使用 apply附加操作&#xff1a;使用 also非空检查&#xff1a;使用 let链式调用&#xff1a;使用 let 或 run Kotlin作用域函数详解 概述 Kotlin提供了5个作用域函数&#xff1a;let、run、with、ap…

嵌入式学习日记(32)Linux下的网络编程

1. 目的不同主机&#xff0c;进程间通信。2. 解决的问题1&#xff09;. 主机与主机之间物理层面必须互联互通。2.&#xff09; 进程与进程在软件层面必须互联互通。IP地址&#xff1a;计算机的软件地址&#xff0c;用来标识计算机设备 MAC地址&#xff1a;计算机的硬件地址&…

C#_接口设计:角色与契约的分离

2.3 接口设计&#xff1a;角色与契约的分离 在软件架构中&#xff0c;接口&#xff08;Interface&#xff09;远不止是一种语言结构。它是一份契约&#xff08;Contract&#xff09;&#xff0c;明确规定了实现者必须提供的能力&#xff0c;以及使用者可以依赖的服务。优秀的接…

vsCode或Cursor 使用remote-ssh插件链接远程终端

一、Remote-SSH介绍Remote-SSH 是 VS Code 官方提供的一个扩展插件&#xff0c;允许开发者通过 SSH 协议连接到远程服务器&#xff0c;并在本地编辑器中直接操作远程文件&#xff0c;实现远程开发。它将本地编辑器的功能&#xff08;如语法高亮、智能提示、调试等&#xff09;与…

C语言实战:从零开始编写一个通用配置文件解析器

资料合集下载链接: ​https://pan.quark.cn/s/472bbdfcd014​ 在软件开发中,我们经常需要将一些可变的参数(如数据库地址、端口号、游戏角色属性等)与代码本身分离,方便日后修改而无需重新编译整个程序。这种存储配置信息的文件,我们称之为配置文件。 一、 什么是配置…

车机两分屏运行Unity制作的效果

目录 效果概述 实现原理 完整实现代码 实际车机集成注意事项 1. 显示系统集成 多屏显示API调用 代码示例&#xff08;AAOS副驾屏显示&#xff09; 2. 性能优化 GPU Instancing 其他优化技术 3. 输入处理 触控处理 物理按键处理 4. 安全规范 驾驶员侧限制 乘客侧…

vivo“空间计算-机器人”生态落下关键一子

出品 | 何玺排版 | 叶媛不出所料&#xff0c;vivo Vision热度很高。从21号下午发布到今天&#xff08;22号&#xff09;&#xff0c;大众围绕vivo Vision探索版展开了多方面的讨论&#xff0c;十分热烈。从讨论来看&#xff0c;大家现在的共识是&#xff0c;MR行业目前还处于起…

Azure TTS Importer:一键导入,将微软TTS语音接入你的阅读软件!

Azure TTS Importer&#xff1a;一键导入&#xff0c;将微软TTS语音接入你的阅读软件&#xff01; 文章来源&#xff1a;Poixe AI 厌倦了机械、生硬的文本朗读&#xff1f;想让你的阅读软件拥有自然流畅的AI语音&#xff1f;今天&#xff0c;我们将为您介绍一款强大且安全的开…

用过redis哪些数据类型?Redis String 类型的底层实现是什么?

Redis 数据类型有哪些&#xff1f; 详细可以查看&#xff1a;数据类型及其应用场景 基本数据类型&#xff1a; String&#xff1a;最常用的一种数据类型&#xff0c;String类型的值可以是字符串、数字或者二进制&#xff0c;但值最大不能超过512MB。一般用于 缓存和计数器 Ha…

大视协作码垛机:颠覆传统制造,开启智能工厂新纪元

在东三省某食品厂的深夜生产线上&#xff0c;码垛作业正有序进行&#xff0c;却不见人影——这不是魔法&#xff0c;而是大视协作码垛机器人带来的现实变革。在工业4.0浪潮席卷全球的今天&#xff0c;智能制造已成为企业生存与发展的必由之路。智能码垛环节作为产线的关键步骤&…