title: Spring Boot 条件注解体系 description: @Conditional 系列注解完全指南,覆盖所有条件注解的用法、组合条件与面试高频追问。
Spring Boot 条件注解体系
候选人小周在面试字节P6时,被问到:
"Spring Boot 的自动配置是怎么控制某个 Bean 在特定条件下才注册的?"
小周说:"用 @Conditional 吧?"面试官追问:"@ConditionalOnProperty 和 @ConditionalOnBean 有什么区别?"小周说:"一个是检查配置,一个是检查 Bean?"
面试官继续追问:"那 @ConditionalOnBean 和 @ConditionalOnMissingBean 呢?它们的执行顺序是什么?"小周彻底卡住了。
面试官又问:"你知道 @ConditionalOnExpression 吗?什么时候用它?"小周答不上来。
【面试官心理】
这道题我用来测试候选人对 Spring Boot 自动配置机制的理解深度。能说出 @Conditional 名字的占 60%,能说出常见条件注解的占 30%,能讲清楚执行顺序和组合条件的只有 10%。这道题是 P6 和 P5 的分水岭。
一、条件注解全景图 🔴
1.1 Spring Boot 2.x 条件注解一览
Spring Boot 在 @Conditional 基础上扩展了大量条件注解,形成了完整的条件体系:
1.2 @ConditionalOnBean vs @ConditionalOnMissingBean
这是最容易混淆的一对。来看一个实战场景:
@ConditionalOnMissingBean 的语义:只有在容器中不存在 DataSource 类型的 Bean 时,才注册这个自动配置的 DataSource。
这意味着:用户自定义的 DataSource Bean 优先级更高,会自动配置的覆盖掉。
最容易踩的坑:@ConditionalOnMissingBean 检查的是当前已注册的 Bean,而不是所有会被注册的 Bean。如果用户定义的 Bean 和自动配置的 Bean 在同一个配置类中,顺序很重要。
1.3 执行顺序的问题
在同一个配置类中,Bean 方法按定义顺序执行,后面的 @ConditionalOnMissingBean 检查的是前面已经注册过的 Bean。
正确做法是:把自动配置和用户配置分离到不同的类中,让 Spring Boot 的 @AutoConfigureAfter 机制来控制顺序。
二、@ConditionalOnClass 的底层原理 🟡
2.1 不是真正加载类
很多人以为 @ConditionalOnClass 是通过 Class.forName 加载类,实际上不是:
这样做的好处:即使 classpath 中没有某个类,JVM 也不会抛出 ClassNotFoundException。因为检查字节码不需要真正加载类到 JVM。
2.2 实际用法
三、@ConditionalOnProperty 深度解析 🟡
3.1 完整参数
3.2 松散命名支持
relaxedNames = true 时支持驼峰和横线分隔的任意组合。
3.3 常用场景
四、组合条件 🟡
4.1 @ConditionalOnBean + @ConditionalOnProperty
实际项目中,最常见的组合是:既要检查 Bean 是否存在,又要检查配置属性:
4.2 @ConditionalOnExpression
当条件判断需要更灵活的逻辑时,使用 SpEL 表达式:
@ConditionalOnExpression 是条件注解中功能最强大的,但也是最容易被滥用的。SpEL 表达式在启动时求值,有一定的性能开销,而且 SpEL 的语法错误很难排查。建议先用 @ConditionalOnProperty 解决,必要时再用 @ConditionalOnExpression。
五、Spring Boot 2.x 到 3.x 的演进 🟢
Spring Boot 3.x 新增了 @ConditionalOnSingleCandidate,用于更精确地判断容器中是否只有一个指定类型的候选 Bean:
六、❌ 错误示范
6.1 混淆 OnBean 和 OnMissingBean
候选人原话:"@ConditionalOnBean 就是当 Bean 存在时生效,@ConditionalOnMissingBean 是当 Bean 不存在时生效。"
问题诊断:
- 理解正确,但不知道执行顺序的问题
- 不知道 @ConditionalOnMissingBean 在同一配置类中的特殊行为
面试官内心 OS:"这个候选人知道两者的表面区别,但不知道 Spring Boot 在处理 Bean 注册顺序时的坑。实践中这个问题非常容易引发排查困难。"
6.2 以为 OnClass 会加载类
候选人原话:"@ConditionalOnClass 是通过 Class.forName 检查类是否存在,不存在就跳过这个配置。"
问题诊断:
- 知道目的但不知道实现原理
- 实际上用的是 ASM 字节码检查,不是 Class.forName
6.3 matchIfMissing 滥用
候选人原话:"配置属性没配的时候,默认就是不生效。"
问题诊断:
- 不理解 matchIfMissing 的作用
- 以为默认行为是"不匹配"
七、面试标准回答
7.1 P5 级别
"Spring Boot 的条件注解用于控制配置类或 Bean 是否生效。常用注解有 @ConditionalOnBean(Bean 存在时)、@ConditionalOnMissingBean(Bean 不存在时)、@ConditionalOnClass(类存在时)、@ConditionalOnProperty(配置满足条件时)。"
7.2 P6 级别
"@ConditionalOnBean 检查容器中是否存在指定类型的 Bean;@ConditionalOnMissingBean 检查不存在时才生效,目的是让用户自定义 Bean 覆盖自动配置。
@ConditionalOnClass 通过 ASM 字节码检查类是否存在,不会触发 ClassNotFoundException。@ConditionalOnProperty 支持 prefix + name + havingValue + matchIfMissing 的组合,matchIfMissing = true 表示配置项不存在时默认匹配。
组合条件使用时要注意:在同一配置类中,Bean 方法按定义顺序执行,后面的 @ConditionalOnMissingBean 检查的是前面已注册的 Bean。建议把用户配置和自动配置分离到不同类中,用 @AutoConfigureBefore/After 控制顺序。"
7.3 P7 级别
"条件注解体系是 Spring Boot 自动配置的基石。我之前排查过一个典型的坑:用户在配置类中先定义了 DataSource Bean,然后又依赖了自动配置的另一个 DataSource Bean,@ConditionalOnMissingBean 判断时发现容器中已经有了,导致自动配置的 Bean 没生效。
Spring Boot 3.x 的 @ConditionalOnSingleCandidate 就是为了解决这类问题——它不仅检查 Bean 是否存在,还检查是否只有一个候选者,避免了误判。
最佳实践是:1. 自动配置类用 @ConditionalOnMissingBean 让用户覆盖;2. 组合条件尽量拆分成多个注解而非 SpEL 表达式;3. 避免在同一配置类中混合用户配置和自动配置;4. 使用 @AutoConfigureBefore/After 控制多个自动配置类之间的顺序。"
【面试官心理】
P7 的回答重点在于"排查过什么坑"和"最佳实践"。能说出 @ConditionalOnSingleCandidate 解决的是什么问题的候选人,说明他对 Spring Boot 3.x 的演进也有关注,这是加分项。