Spring Boot 源码分析 - 剖析 @SpringBootApplication 注解
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读
Spring Boot 版本:2.2.x
最好对 Spring 源码有一定的了解,可以先查看我的 《死磕 Spring 之 IoC 篇 - 文章导读》 系列文章
如果该篇内容对您有帮助,麻烦点击一下“推荐”,也可以关注博主,感激不尽~
该系列其他文章请查看:《精尽 Spring Boot 源码分析 - 文章导读》
概述
现如今,Spring Boot
在许多中大型企业中被普及,想必大家对于 @SpringBootApplication
并不陌生,这个注解通常标注在我们应用的启动类上面,标记是一个 Spring Boot 应用,同时开启自动配置的功能,那么你是否有深入了解过该注解呢?没有的话,或许这篇文章可以让你对它有一个新的认识。
提示:
@EnableAutoConfiguration
是开启自动配置功能的模块驱动注解,是 Spring Boot 的核心注解整篇文章主要是对这个注解,也就是 Spring Boot 的自动配置功能进行展述
@SpringBootApplication
org.springframework.boot.autoconfigure.SpringBootApplication
注解在 Spring Boot 的 spring-boot-autoconfigre
子模块下,当我们引入 spring-boot-starter
模块后会自动引入该子模块
该注解是一个组合注解,如下:
@SpringBootApplication
注解就是一个组合注解,里面的每个配置都是元注解中对应的属性,上面已做描述
该注解上面的 @Inherited
元注解是 Java 提供的,标注后表示当前注解定义在某个类上时,其子类会继承该注解,我们一起来看看其他三个注解
@SpringBootConfiguration
org.springframework.boot.SpringBootConfiguration
注解,Spring Boot 自定义注解
该注解很简单,上面标注了 @Configuration
元注解,所以作用相同,同样是将一个类标注为配置类,能够作为一个 Bean 被 Spring IoC 容器管理
至于为什么不直接使用 @Configuration
注解呢,我想这应该是 领域驱动设计 中的一种思想,可以使得 Spring Boot 更加灵活,总有它的用武之地
领域驱动设计:Domain-Driven Design,简称 DDD。过去系统分析和系统设计都是分离的,这样割裂的结果导致需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。DDD 则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。
@ComponentScan
org.springframework.context.annotation.ComponentScan
注解,Spring 注解,扫描指定路径下的标有 @Component
注解的类,解析成 Bean 被 Spring IoC 容器管理
想深入了解该注解的小伙伴可以查看我前面对 Spring IoC 进行源码分析的文章:
该注解通常需要和 @Configuration
注解一起使用,因为需要先被当做一个配置类,然后解析到上面有 @ComponentScan
注解后则处理该注解,通过 ClassPathBeanDefinitionScanner 扫描器去扫描指定路径下标注了 @Component
注解的类,将他们解析成 BeanDefinition(Bean 的前身),后续则会生成对应的 Bean 被 Spring IoC 容器管理
当然,如果该注解没有通过 basePackages
指定路径,Spring 会选在以该注解标注的类所在的包作为基础路径,然后扫描包下面的这些类
@EnableAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration
注解,Spring Boot 自定义注解,用于驱动 Spring Boot 自动配置模块
对于 Spring 中的模块驱动注解的实现都是通过 @Import
注解来实现的
模块驱动注解通常需要结合 @Configuration
注解一起使用,因为需要先被当做一个配置类,然后解析到上面有 @Import
注解后则进行处理,对于 @Import
注解的值有三种情况:
-
该 Class 对象实现了
ImportSelector
接口,调用它的selectImports(..)
方法获取需要被处理的 Class 对象的名称,也就是可以将它们作为一个 Bean 被 Spring IoC 管理- 该 Class 对象实现了
DeferredImportSelector
接口,和上者的执行时机不同,在所有配置类处理完后再执行,且支持@Order
排序
- 该 Class 对象实现了
-
该 Class 对象实现了
ImportBeanDefinitionRegistrar
接口,会调用它的registerBeanDefinitions(..)
方法,自定义地往 BeanDefinitionRegistry 注册中心注册 BeanDefinition(Bean 的前身) -
该 Class 对象是一个
@Configuration
配置类,会将这个类作为一个 Bean 被 Spring IoC 管理
对于 @Import
注解不熟悉的小伙伴可查看我前面的 《死磕Spring之IoC篇 - @Bean 等注解的实现原理》 这篇文章
这里的 @EnableAutoConfiguration
自动配置模块驱动注解,通过 @Import
导入 AutoConfigurationImportSelector 这个类(实现了 DeferredImportSelector
接口)来驱动 Spring Boot 的自动配置模块,下面会进行分析
@AutoConfigurationPackage
我们注意到 @EnableAutoConfiguration
注解上面还有一个 @AutoConfigurationPackage
元注解,它的作用就是注册一个 Bean,保存了当前注解标注的类所在包路径
同样这里使用了 @Import
注解来实现的,对应的是一个 AutoConfigurationPackages.Registrar
内部类,如下:
比较简单,这里直接跳过了
自动配置
在开始之前,我们先来了解一下 Spring Boot 的自动配置,就是通过引入某个功能的相关 jar
包依赖后,Spring Boot 能够自动配置应用程序,让我们很方便的使用该功能
-
例如当你引入
spring-boot-starter-aop
后,会自动引入 AOP 相关的jar
包依赖,那么在spring-boot-autoconfigure
中有一个AopAutoConfiguration
自动配置类会自动驱动整个 AOP 模块 -
例如当你引入
spring-boot-starter-web
后,会自动引入 Spring MVC、Tomcat 相关的jar
包依赖,那么在spring-boot-autoconfigure
中会有相应的自动配置类会自动配置 Spring MVC
当然,还有许多自动配置类,结合这 Spring Boot 的 Starter 模块,让许多功能或者第三方 jar
包能够很简便的和 Spring Boot 整合在一起使用
现在很多开源框架都提供了对应的 Spring Boot Starter 模块,能够更好的整合 Spring Boot,当你熟悉自动配置功能后,你也可以很轻松的写一个 Starter 包供他人使用😃😃😃
这里先提前剧透一下,自动配置类为什么在你引入相关
jar
包后会自动配置对应的模块呢?主要就是拓展了 Spring 的 Condition,例如
@ConditionalOnClass
注解,当存在指定的 Class 对象时才注入某个 Bean同时也可以再结合
@EnableXxx
模块注解,通过@Import
注解驱动某个模块具体细节,请继续往下看
AutoConfigurationImportSelector
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
,实现了 DeferredImportSelector 接口,是 @EnableAutoConfiguration
注解驱动自动配置模块的核心类
直接看到实现的 ImportSelector 接口的方法
1. selectImports 方法
selectImports(AnnotationMetadata)
方法,返回需要注入的 Bean 的类名称
过程如下:
- 如果通过
spring.boot.enableautoconfiguration
配置关闭了自动配置功能,那么直接返回一个空数组 - 解析
META-INF/spring-autoconfigure-metadata.properties
文件,生成一个 AutoConfigurationMetadata 自动配置类元数据对象 - 调用
getAutoConfigurationEntry(..)
方法, 从所有的META-INF/spring.factories
文件中找到@EnableAutoConfiguration
注解对应的类(需要自动配置的类),会进行过滤处理,然后封装在一个AutoConfigurationEntry
对象中 - 返回所有需要自动配置的类
上面第 2
步调用的方法:
这一步困惑了我很久,因为在 Spring Boot 工程中根本找不到 META-INF/spring-autoconfigure-metadata.properties
文件,而我们自己也没有配置过,但是在我们自己的 Spring Boot 应用依赖的 spring-boot-autoconfigure.jar
包里面又存在这个文件,如下:

那么这是为什么呢?经过我长时间的 Google,找到了答案
-
在引入
spring-boot-autoconfigure-processor
工具模块依赖后,其中会通过 Java SPI 机制引入 AutoConfigureAnnotationProcessor 注解处理器在编译阶段进行相关处理 -
其中
spring-boot-autoconfigure
模块会引入该工具模块(不具有传递性),那么 Spring Boot 在编译spring-boot-autoconfigure
这个jar
包的时候,在编译阶段会扫描到带有@ConditionalOnClass
等注解的.class
文件,也就是自动配置类,然后将自动配置类的一些信息保存至META-INF/spring-autoconfigure-metadata.properties
文件中 -
文件中保存了
自动配置类类名.注解简称
-->注解中的值(逗号分隔)
,自动配置类类名
-->空字符串
-
当然,你自己写的 Spring Boot Starter 中的自动配置模块也可以引入这个 Spring Boot 提供的插件
得到的结论:
至于为什么这么做,是因为 Spring Boot 提供的自动配置类比较多,而我们不可能使用到很多自动配置功能,大部分都没必要,如果每次你启动应用的过程中,都需要一个一个去解析他们上面的 Conditional 注解,那么肯定会有不少的性能损耗
这里,Spring Boot 做了一个优化,通过自己提供的工具,在编译阶段将自动配置类的一些注解信息保存在一个 properties
文件中,这样一来,在你启动应用的过程中,就可以直接读取该文件中的信息,提前过滤掉一些自动配置类,相比于每次都去解析它们所有的注解,性能提升不少
2. getAutoConfigurationEntry 方法
getAutoConfigurationEntry(AutoConfigurationMetadata, AnnotationMetadata)
方法,返回符合条件的自动配置类,如下:
过程如下:
-
如果通过
spring.boot.enableautoconfiguration
配置关闭了自动配置功能,则返回一个“空”的对象 -
获取
@EnableAutoConfiguration
注解的配置信息 -
从所有的
META-INF/spring.factories
文件中找到@EnableAutoConfiguration
注解对应的类(需要自动配置的类),保存在configurations
集合中这个 SpringFactoriesLoader 是由 Spring 提供的一个类
-
对所有的自动配置类进行去重
-
获取需要排除的自动配置类,可通过
@EnableAutoConfiguration
注解的exclude
和excludeName
配置,也可以通过spring.autoconfigure.exclude
配置 -
处理
exclusions
中特殊的类名称,保证能够排除它 -
从
configurations
中将exclusions
需要排除的自动配置类移除 -
调用
filter(..)
方法, 目的就是过滤掉一些不符合 Condition 条件的自动配置类,和在 1. selectImports 方法 小节中讲到的性能优化有关哦 -
从
META-INF/spring.factories
找到所有的 AutoConfigurationImportListener 事件监听器,触发每个监听器去处理 AutoConfigurationImportEvent 事件,该事件中包含了configurations
和exclusions
Spring Boot 中配置了一个监听器,目的就是将
configurations
和exclusions
保存至 AutoConfigurationImportEvent 对象中,并注册到 IoC 容器中,名称为autoConfigurationReport
,这样一来我们可以注入这个 Bean 获取到自动配置类信息 -
将所有的自动配置类封装成一个 AutoConfigurationEntry 对象,并返回
整个过程不复杂,关键在于上面的第 3
步和第 8
步,先从所有的 META-INF/spring.factories
文件中找到 @EnableAutoConfiguration
注解对应的类(需要自动配置的类),然后进行过滤
3. filter 方法
filter(List<String>, AutoConfigurationMetadata)
方法,过滤一些自动配置类
我们得先知道这两个入参:
- 所有的自动配置类名称
META-INF/spring-autoconfigure-metadata.properties
文件保存的 Spring Boot 的自动配置类的注解元信息(Sprng Boot 编译时生成的)
这里的目的就是根据 2
里面的注解元信息,先过滤掉一些自动配置类
过程如下:
-
将自动配置类保存至
candidates
数组中 -
从
META-INF/spring.factories
找到所有的AutoConfigurationImportFilter
对candidates
进行过滤处理- Aware 回调
- 对
candidates
进行匹配处理,获取所有的匹配结果,注意,这里传入了AutoConfigurationMetadata
对象 - 遍历匹配结果,将不匹配的自动配置类至空
-
如果没有不匹配的结果则全部返回
-
获取到所有匹配的自动配置类,并返回
关键在于上面的第 2
步,通过 Spring Boot 自己扩展的几个自动配置类过滤器进行过滤,由于这部分内容和 Spring Boot 拓展 Condition 相关,放入下篇文章进行分析
下面我们一起来看看上面 1. selectImports 方法 小节中讲到的性能优化,META-INF/spring-autoconfigure-metadata.properties
文件是如何生成的,文件的内容又是什么
AutoConfigureAnnotationProcessor
org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor
,Spring Boot 的 spring-boot-autoconfigure-processor
工具模块中的自动配置类的注解处理器,在编译阶段扫描自动配置类的注解元信息,并将他们保存至一个 properties
文件中
AbstractProcessor 是 JDK 1.6 引入的一个抽象类,支持在编译阶段进行处理,在构造器中做了以下事情:
-
创建一个 Map 集合
-
将指定注解的简称和全称之间的对应关系保存至第
1
步创建的 Map 中 -
将
1.1
的 Map 转换成不可修改的 UnmodifiableMap 集合,赋值给annotations
-
-
创建一个 Map 集合
-
将指定注解的简称和对应的 ValueExtractor 对象保存至第
2
步创建的 Map 中 -
将
2.1
的 Map 转换成不可修改的 UnmodifiableMap 集合,赋值给valueExtractors
-
getSupportedSourceVersion 方法
返回支持的 Java 版本
process 方法
处理过程
过程如下:
-
遍历上面的几个
@Conditional
注解和几个定义自动配置类顺序的注解,依次进行处理- 调用
process(..)
重载方法,对支持的注解进行处理,也就是找到所有标注了该注解的类,然后解析出该注解的值,保存至 Properties
- 调用
-
如果处理完成
-
调用
writeProperties()
方法,将 Properties 写入META-INF/spring-autoconfigure-metadata.properties
文件
-
上面的第 1.1
步处理后的 Properties 包含以下内容:
自动配置类的类名.注解简称
-->注解中的值(逗号分隔)
自动配置类的类名
-->空字符串
通过后续写入的文件,避免你每次启动 Spring Boot 应用都要去解析自动配置类上面的注解,从而提高应用启动时的效率
这么一想,Spring Boot 设计的太棒了,所以你自己写的 Spring Boot Starter 中的自动配置模块也可以引入这个 Spring Boot 提供的插件
process 重载方法
过程如下:
- 获取到这个注解名称对应的 Java 类型
- 如果存在该注解,则从 RoundEnvironment 中获取标注了该注解的所有 Element 元素,进行遍历
- 获取这个 Element 元素 innermost 最深处的 Element
- 如果最深处的 Element 的类型是 PACKAGE 包,那么表示这个元素是一个类,则进行处理
- 调用
processElement(..)
方法,解析这个类上面annotationName
注解的信息,并保存至properties
中
- 调用
processElement 方法
过程如下:
-
获取这个类的名称
-
获取这个类上面的
annotationName
类型的注解信息 -
获取这个注解中的值
-
往
properties
中添加类名.注解简称
-->注解中的值(逗号分隔)
-
往
properties
中添加类名
-->空字符串
总结
本文分析了 @SpringBootApplication
组合注解,它是 @SpringBootConfiguration
、@ComponentScan
和 @EnableAutoConfiguration
几个注解的组合注解,对于前两个注解我想你并不陌生,分析 Spring 源码的时候差不多已经讲过,最后一个注解则是 Spring Boot 自动配置功能的驱动注解,也是本文讲述的一个重点。
@EnableAutoConfiguration
注解的实现原理并不复杂,借助于 @Import
注解,从所有 META-INF/spring.factories
文件中找到 org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的值,例如:

会将这些自动配置类作为一个 Bean 尝试注入到 Spring IoC 容器中,注入的时候 Spring 会通过 @Conditional
注解判断是否符合条件,因为并不是所有的自动配置类都满足条件。当然,Spring Boot 对 @Conditional
注解进行了扩展,例如 @ConditionalOnClass
可以指定必须存在哪些 Class 对象才注入这个 Bean。
Spring Boot 会借助 spring-boot-autoconfigure-processor
工具模块在编译阶段将自己的自动配置类的注解元信息保存至一个 properties
文件中,避免每次启动应用都要去解析这么多自动配置类上面的注解。同时会通过几个过滤器根据这个 properties
文件过滤掉一些自动配置类,具体怎么过滤的会在下面文章讲到。
好了,总结下来就是 Spring Boot 会从 META-INF/spring.factories
文件中找到配置的自动配置类,然后根据 Condition 条件进行注入,如果注入的话则可以通过 @EnableXxx
模块驱动注解驱动某个模块,例如 Spring AOP 模块。那么关于 Spring Boot 对 Spring Condition 的扩展在下篇文章进行分析。
__EOF__

本文链接:https://www.cnblogs.com/lifullmoon/p/14957751.html
关于博主:本着学习与分享的目的,将持续不断的进行知识分享。望各位到访看客如有喜欢的文章,可以点击一下“推荐”,若有不同建议或者意见,也请不吝赐教,博主感激不尽。另外,欢迎转载博主的文章,请务必依据文章下方的版权声明转载。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义