@Conditional 本身只是条件开关,真正逻辑由 Condition 实现类完成;Spring Boot 推荐直接使用其封装注解:@ConditionalOnProperty(按配置控制)、@ConditionalOnClass(按类路径依赖)、@ConditionalOnMissingBean(按容器中 Bean 缺失情况),三者组合覆盖绝大多数场景。

怎么用 @Conditional 实现环境或依赖驱动的 Bean 创建
直接说结论:@Conditional 本身不处理具体逻辑,它只是“开关”,真正干活的是你写的 Condition 实现类。Spring Boot 里更常用的是它的封装版——比如 @ConditionalOnProperty(看配置)、@ConditionalOnClass(看依赖)、@ConditionalOnBean(看已有 Bean)。这些才是日常开发中真正拿来就用、不写额外类的主力。
@ConditionalOnProperty:按 application.yml 中的开关控制 Bean
这是最常被用来做“环境差异化注入”的注解,比如 dev 环境启用 mock 服务,prod 环境禁用。
- 配置项必须存在且值为
true才生效(默认行为),例如:@ConditionalOnProperty("feature.mock.enabled") - 如果想让“没配这个 key”也走这条路,加
matchIfMissing = true;但注意:一旦设了havingValue = "false",那只有显式配成false才匹配,不是“非 true 即 false” - 别用
value和name同时指定同一组键;它们是等价的,混用容易误判 - 示例:
@Bean<br>@ConditionalOnProperty(name = "app.auth.mode", havingValue = "jwt", matchIfMissing = false)<br>public TokenService jwtTokenService() { return new JwtTokenService(); }
@ConditionalOnClass 和 @ConditionalOnMissingBean:依赖存在性才是关键判断点
这两个组合起来,是 Spring Boot 自动配置的底层逻辑。你写自定义 starter 或模块化组件时,靠它们避免启动失败或 Bean 冲突。
-
@ConditionalOnClass检查的是类路径(classpath)有没有某个类,不是“是否已加载为 Bean”。比如检查RedisTemplate.class存在,不代表 Redis 连接一定通 -
@ConditionalOnMissingBean是容器级判断,它会在所有@Configuration类都扫描完、用户自定义 Bean 注入完成后才执行——所以务必把它放在自动配置类里,而不是普通@Component - 常见错误:把
@ConditionalOnMissingBean放在普通@Component类里,结果发现“明明没定义 XXXBean,它还是没创建”,原因就是扫描顺序错乱,条件提前求值了 - 建议加
search = SearchStrategy.CURRENT显式限定搜索范围,避免父上下文干扰
为什么不要轻易自己写 Condition 实现类
除非你要判断的东西完全不在 Spring Boot 内置注解覆盖范围内(比如读取某个外部文件内容、调用一次 HTTP 接口),否则真没必要手写 Condition。
- 手写
matches()方法时,ConditionContext里的getEnvironment()和getBeanFactory()很容易空指针——因为此时容器还没初始化完,部分能力不可用 - 调试困难:Condition 的执行发生在 BeanDefinition 注册阶段,日志不会出现在常规启动流程里,得加断点或启用
DEBUG级别日志才能看到 - 性能隐患:如果在
matches()里做了耗时操作(如 IO、反射扫描全类路径),会拖慢整个启动过程 - 绝大多数场景,
@ConditionalOnProperty+@ConditionalOnClass+@ConditionalOnMissingBean三者组合已经够用
真正容易被忽略的是:这些注解的生效时机比你想的更早,而且彼此之间有隐含依赖关系。比如 @ConditionalOnBean 要求目标 Bean 已注册,但它自己又可能被 @ConditionalOnClass 控制——这时候顺序和层级就变得特别关键。别只盯着单个注解怎么写,得通盘看配置类的加载顺序和 Bean 生命周期阶段。










