Spring Boot 源码分析 - Condition 接口的扩展
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读
Spring Boot 版本:2.2.x
最好对 Spring 源码有一定的了解,可以先查看我的 《死磕 Spring 之 IoC 篇 - 文章导读》 系列文章
如果该篇内容对您有帮助,麻烦点击一下“推荐”,也可以关注博主,感激不尽~
该系列其他文章请查看:《精尽 Spring Boot 源码分析 - 文章导读》
概述
在上一篇《剖析 @SpringBootApplication 注解》文章分析了 Spring Boot 的自动配置功能,在通过 @EnableAutoConfiguration
注解驱动整个自动配置模块的过程中,并不是所有的自动配置类都需要被注入,不同的自动配置类需要满足一定条件后,才应该进行自动配置。
那么 Spring Boot 怎么知道满足一定条件呢?Spring Boot 对 Spring 的 Condition 接口进行了扩展,然后结合自定义的注解,则可以判断自动配置类是否符合条件。
例如 @ConditionalOnClass
可以指定必须存在哪些 Class 对象才注入这个 Bean。
那么接下来,我们一起来看看 Spring 的 Condition 接口以及 Spring Boot 对其的扩展
Condition 演进史
Profile 的出场
在 Spring 3.1 的版本,为了满足不同环境注册不同的 Bean ,引入了 @Profile
注解。例如:
- 在测试环境下,我们注册单机 MySQL 的 DataSource Bean
- 在生产环境下,我们注册集群 MySQL 的 DataSource Bean
Spring 3.1.x 的 @Profile
注解如下:
可以看到,最开始 @Profile
注解并没有结合 @Conditional
注解一起使用,而是在后续版本才引入的
Condition 的出现
在 Spring 4.0 的版本,出现了 Condition 功能,体现在 org.springframework.context.annotation.Condition
接口,如下:
函数式接口,只有一个 matches(..)
方法,判断是否匹配,从入参中可以知道,它是和注解配合一起实现 Condition 功能的,也就是 @Conditional
注解,如下:
随之 @Profile
注解也进行了修改,和 @Conditional
注解配合使用
Spring 5.1.x 的 @Profile
注解如下:
这里指定的的 Condition 实现类是 ProfileCondition,如下:
逻辑很简答,从当前 Spring 应用上下文的 Environment 中判断 @Profile
指定的环境是否被激活,被激活了表示匹配成功,则注入对应的 Bean,否则,不进行操作
但是 Spring 本身提供的 Condition 实现类不多,只有一个 ProfileCondition 对象
SpringBootCondition 的进击
Spring Boot 为了满足更加丰富的 Condition 场景,对 Spring 的 Condition 接口进行了扩展,提供更多的实现类,如下:

上面仅列出了部分 SpringBootCondition 的子类,同时这些子类与对应的注解配置一起使用
@ConditionalOnClass
:必须都存在指定的 Class 对象们@ConditionalOnMissingClass
:指定的 Class 对象们必须都不存在@ConditionalOnBean
:必须都存在指定的 Bean 们@ConditionalOnMissingBean
:指定的 Bean 们必须都不存在@ConditionalOnSingleCandidate
:必须存在指定的 Bean@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnWebApplication
:当前的 WEB 应用类型是否在指定的范围内(ANY、SERVLET、REACTIVE)@ConditionalOnNotWebApplication
:不是 WEB 应用类型
上面列出了 Spring Boot 中常见的几种 @ConditionXxx
注解,他们都配合 @Conditional
注解与对应的 Condition 实现类一起使用,提供了非常丰富的 Condition 场景
Condition 在哪生效?
Spring 提供了 Condition 接口以及 @Conditional
注解,那么在 Spring 中哪里体现,或者说是哪里进行判断的呢?
其实我在 《死磕Spring之IoC篇 - @Bean 等注解的实现原理》 这篇文章中有提到过,我们稍微回顾一下,有两种情况:
- 通过
@Component
注解(及派生注解)标注的 Bean @Configuration
标注的配置类中的@Bean
标注的方法 Bean
普通 Bean
第一种情况是在 Spring 扫描指定路径下的 .class 文件解析成对应的 BeanDefinition(Bean 的前身)时,会根据 @Conditional
注解判断是否符合条件,如下:
上面只是简单的提一下,可以看到会通过 ConditionEvaluator 计算器进行计算,判断是否满足条件
配置类
第二种情况是 Spring 会对 配置类进行处理,扫描到带有 @Bean
注解的方法,尝试解析成 BeanDefinition(Bean 的前身)时,会根据 @Conditional
注解判断是否符合条件,如下:
上面只是简单的提一下,可以看到会通过 TrackedConditionEvaluator 计算器进行计算,判断是否满足条件
这里提一下,对于 @Bean
标注的方法,会得到 CGLIB 的提升,也就是返回的是一个代理对象,设置一个拦截器专门对 @Bean
方法进行拦截处理,通过依赖查找的方式从 IoC 容器中获取 Bean 对象,如果是单例 Bean,那么每次都是返回同一个对象,所以当主动调用这个方法时获取到的都是同一个对象。
SpringBootCondition
org.springframework.boot.autoconfigure.condition.SpringBootCondition
抽象类,实现了 Condition 接口,Spring Boot 扩展 Condition 的抽象基类,主要用于打印相应的日志,并记录每次的匹配结果,如下:
实现的 Condition 接口方法处理过程如下:
-
从注解元信息中获取所标注的
类名
(或者类名#方法名
) -
调用
getMatchOutcome(..)
方法,获取匹配结果(包含匹配消息),抽象方法,交由子类实现 -
打印匹配日志
-
向 ConditionEvaluationReport 中记录本次的匹配结果
-
返回匹配结果
SpringBootCondition 的实现类
OnClassCondition
org.springframework.boot.autoconfigure.condition.OnClassCondition
,继承 SpringBootCondition 抽象类,如下:
判断是否匹配的过程如下:
-
获取这个类上面的
@ConditionalOnClass
注解的值,也就是哪些 Class 对象必须存在-
找到这些 Class 对象中哪些是不存在的
-
如果存在不存在的,那么不符合条件,返回不匹配
-
添加
@ConditionalOnClass
满足条件的匹配信息
-
-
获取这个类上面的
@ConditionalOnMissingClass
注解的值,也就是这些 Class 对象必须都不存在- 找到这些 Class 对象中哪些是存在的,和上面的
1.1
差不多,只不过这里传的是 ClassNameFilter.PRESENT 过滤器 - 如果有一个存在,那么不符合条件,返回不匹配
- 添加
@ConditionalOnMissingClass
满足条件的匹配信息
- 找到这些 Class 对象中哪些是存在的,和上面的
-
返回符合条件的结果
上面使用到的 ClassNameFilter 如下:
逻辑很简单,就是判断 Class 对象是否存在或者不存在
其它实现类
关于 SpringBootCondition 其他的实现类逻辑都差不多,感兴趣的可以去看看
回顾自动配置
在上一篇《剖析 @SpringBootApplication 注解》 文章分析通过 @EnableAutoConfiguration
注解驱动整个自动配置模块的过程中,会通过指定的 AutoConfigurationImportFilter 对所有的自动配置类进行过滤,满足条件才进行自动配置
可以回顾一下上一篇文章的 2. getAutoConfigurationEntry 方法 小节和 3. filter 方法 小节
我们看到第 8
步,调用 filter(..)
方法, 目的就是过滤掉一些不符合 Condition 条件的自动配置类
可以看到第 2
步,会从 META-INF/spring.factories
中找到对应的 AutoConfigurationImportFilter 实现类对所有的自动配置类进行过滤
这里,我们一定要注意到入参中的 AutoConfigurationMetadata
对象,它里面保存了 META-INF/spring-autoconfigure-metadata.properties
文件中 Spring Boot 的自动配置类的注解元信息(Sprng Boot 编译时生成的),如何来的请回顾上一篇文章
AutoConfigurationImportFilter
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
接口,用于过滤掉无需自动引入的自动配置类
可以看到它的注释,因为自动配置类会很多,如果无需使用,而创建对应的 Bean(字节码)到 JVM 内存中,将是一种浪费

可以看到它的最终实现类,都是构建在 SpringBootCondition 之上。😈 不过这也很正常,因为 Condition 本身提供的一个功能,就是作为配置类(Configuration)是否能够符合条件被引入。
FilteringSpringBootCondition
org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition
,继承 SpringBootCondition 抽象类,实现 AutoConfigurationImportFilter 接口,作为具有 AutoConfigurationImportFilter 功能的 SpringBootCondition 的抽象基类。
可以看到,这实现方法和 SpringBootCondition 的 match(..)
方法很想,他们的入参不同,不要搞混了,这里的处理过程如下:
- 从 Spring 应用上下文中获取 ConditionEvaluationReport 对象
- 调用
getOutcomes(..)
抽象方法,获取所有自动配置类的匹配结果,空方法,交由子类实现 - 将自动配置类的匹配结果保存至一个
boolean[]
数组中,并将匹配结果一一保存至 ConditionEvaluationReport 中- 注意这里匹配结果为空也表示匹配成功
- 返回所有自动配置类是否满足条件的结果数组
FilteringSpringBootCondition 的实现类
OnClassCondition
org.springframework.boot.autoconfigure.condition.OnClassCondition
,继承 FilteringSpringBootCondition 抽象类,如下:
过滤所有自动配置类的的过程如下:
考虑到自动配置类上面的 @Conditional
相关注解比较多,所以采用多线程以提升效率。经过测试使用,使用两个线程的效率是最高的,所以会尝试将 autoConfigurationClasses
一分为二进行处理
- 如果 JVM 可用的处理器不止一个,那么这里用两个线程去处理
- 对
autoConfigurationClasses
所有的自动配置类进行处理,这里是对@ConditionalOnClass
注解进行处理,必须存在指定 Class 类对象
- 对
- 否则,就是单核处理,当前线程去处理
- 创建一个匹配处理器
outcomesResolver
- 返回
outcomesResolver
的执行结果,这里是对@ConditionalOnClass
注解进行处理,必须存在指定 Class 类对象
- 创建一个匹配处理器
resolveOutcomesThreaded 方法
逻辑很简单,就是同时两个线程分半处理,还是通过 StandardOutcomesResolver 匹配处理器来处理
StandardOutcomesResolver 处理器
getOutcomes 方法
逻辑比较简单,先从 AutoConfigurationMetadata 这个对象中找到这个自动配置类 @ConditionalOnClass
注解的值,如下:
如果没有该注解,匹配结果为空,也表示匹配成功,存在该注解,则对指定的 Class 对象进行判断,如果有一个 Class 对象不存在,匹配结果则是不匹配,所以这个 AutoConfigurationMetadata 对于过滤自动配置类很关键
其它实现类
另外两个 OnBeanCondition 和 OnWebApplicationCondition 实现类的原理差不多,感兴趣的可以去看看
AutoConfigurationMetadata
org.springframework.boot.autoconfigure.AutoConfigurationMetadata
接口,仅有一个 PropertiesAutoConfigurationMetadata 实现类
这个对象很关键,在文中提到不少次数,是解析 META-INF/spring-autoconfigure-metadata.properties
文件生成的对象,里面保存了 Spring Boot 中自动配置类的注解元数据
至于如何解析这个文件,如何生成该对象,这里不在讲述,可以回顾上一篇《剖析 @SpringBootApplication 注解》 文章的 AutoConfigureAnnotationProcessor 小节
下面列举文件中的部分内容
看到这个文件是不是明白了,判断是否要引入 DispatcherServletAutoConfiguration
这个自动配置类,从这个 properties
文件中找到它的 @ConditionalOnClass
注解的值,然后判断指定的 Class 对象们是否都存在,不存在则表示不需要引入这个自动配置类
当然,还有 @OnBeanCondition
和 @OnWebApplicationCondition
两个注解的判断
总结
本文分析了 Spring 的 Condition 接口,配合 @Conditional
注解的使用,可以判断某个 Class 对象是否有必要生成一个 Bean 被 Spring IoC 容器管理。
由于在 Spring 中 Condition 接口的实现类就一个,Spring Boot 则对 Condition 接口进行扩展,丰富了更多的使用场景,其内部主要用于自动配置类。同时,在 Spring Boot 中提供了很多 Condition 相关的注解,配合 @Conditional
注解一起使用,就能将满足条件的自动配置类引入进来。例如 @OnClassCondition
注解标注的配置类必须存在所有的指定的 Class 对象们,才将其生成一个 Bean 被 Spring IoC 容器管理。
在 @EnableAutoConfiguration
注解驱动自动配置模块的过程中,会通过 AutoConfigurationImportFilter
过滤掉一些不满足条件的自动配置类,原理和 Condition 差不多,主要通过上面这个 AutoConfigurationMetadata
类,再结合不同的 AutoConfigurationImportFilter
实现类实现的。
__EOF__

本文链接:https://www.cnblogs.com/lifullmoon/p/14957771.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 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义