在使用 Spring Boot 的过程中,很多人会接触到:
@Conditional@ConditionalOnProperty@ConditionalOnClass
但往往只是"会用",却不知道它到底解决什么问题。
这篇文章帮你彻底搞清楚:
👉 @Conditional 到底是什么、什么时候触发、怎么用、适合用在哪
一、什么是 @Conditional(本质一句话)
👉 @Conditional = 条件装配机制
根据条件,决定某个 Bean 是否被加载到 Spring 容器中
二、先纠正一个常见误区(非常重要)
很多人第一反应是:
❌ 用来做环境切换(dev / test / prod)
👉 这是不准确的!
正确分工是:
| 功能 | 用什么 |
|---|---|
| 多环境切换 | @Profile |
| 条件装配 | @Conditional |
👉 举个对比:
环境切换(Profile)
java
@Profile("dev")
@Bean
public ApiService mockApi() {}
条件控制(Conditional)
java
@Bean
@ConditionalOnProperty(name = "feature.pay-enabled", havingValue = "true")
public PayService payService() {}
👉 总结一句:
Profile 控制"在哪个环境生效",Conditional 控制"这个功能有没有"
三、@Conditional 在启动流程中的位置(核心)
Spring 启动流程中:
java
扫描类
↓
解析配置类
↓
⭐ 执行 @Conditional 判断
↓
注册 BeanDefinition
↓
创建 Bean
👉 关键点:
❗ @Conditional 发生在"Bean 创建之前"
更准确一点:
扫描到类
↓
检查 @Conditional
↓
true → 注册 BeanDefinition
false → 直接跳过(相当于不存在)
四、核心原理(你必须理解)
@Conditional 本质是:
@Conditional(XXXCondition.class)
你要实现一个类:
java
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true; // true 加载,false 不加载
}
}
👉 这个 matches() 决定:
| 返回值 | 结果 |
|---|---|
| true | 加载 Bean |
| false | 不加载 |
五、最常用的几个注解(你实际用这些)
Spring Boot 已经帮你封装好了:
1️⃣ @ConditionalOnProperty(最常用 ⭐)
java
@Bean
@ConditionalOnProperty(name = "feature.pay-enabled", havingValue = "true")
public PayService payService() {}
yaml
java
feature:
pay-enabled: true
👉 作用:
配置开关控制功能是否开启
2️⃣ @ConditionalOnClass
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
👉 用于自动配置(判断依赖是否存在)
3️⃣ @ConditionalOnMissingBean
@ConditionalOnMissingBean(PayService.class)
👉 如果没有 Bean 才创建(避免覆盖)
六、典型使用场景(企业真实用法)
✅ 1. 功能开关(最常见)
java
feature:
sms-enabled: false
java
@ConditionalOnProperty(name = "feature.sms-enabled", havingValue = "true")
@Bean
public SmsService smsService() {}
👉 效果:
- false → 系统没有短信功能
- true → 才有
✅ 2. 第三方接入控制
java
pay:
wechat-enabled: true
java
@ConditionalOnProperty(name = "pay.wechat-enabled", havingValue = "true")
@Bean
public WechatPayService wechatPayService() {}
✅ 3. 自动配置(Spring Boot 核心机制)
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
👉 Spring Boot 就是这么"按需加载"的
七、一个非常关键的理解(必须记住)
👉 @Conditional 不是权限控制
❌ 错误理解
用户是 VIP → 用 @Conditional 控制 ❌
✅ 正确做法
第一层:系统能力(Conditional)
系统有没有 VIP 功能
第二层:用户权限(业务代码)
java
if (user.isVip()) {
vipService.use();
}
👉 总结:
@Conditional 控制"系统有没有功能",不是"用户能不能用"
八、和 Android 的对比(帮助理解)
| Android | Spring |
|---|---|
| Flavor | Profile |
| BuildConfig | Environment |
| if 判断 | @Conditional |
| 不同 APK | 不同 Bean |
👉 本质差异:
| 类型 | 时机 |
|---|---|
| Android Flavor | 编译期 |
| Spring Conditional | 运行期(启动时) |
九、实战推荐写法(你可以直接用)
feature:
pay-enabled: true
sms-enabled: false
vip-enabled: true
java
@ConditionalOnProperty(name = "feature.pay-enabled", havingValue = "true")
@Bean
public PayService payService() {}
👉 搭配:
java
@Autowired(required = false)
private PayService payService;
十、面试标准回答(直接背)
@Conditional 是 Spring 的条件装配机制,在容器启动时根据条件决定 Bean 是否注册。它通常用于功能开关、自动配置以及第三方组件加载。与 Profile 不同,Profile 主要用于环境切换,而 Conditional 更偏向于控制系统能力是否存在。
十一、总结(最重要)
@Conditional 的本质:
↓
控制 Bean 是否存在
发生时机:
↓
容器启动 → 注册 BeanDefinition 阶段
适用场景:
↓
功能开关 / 自动配置 / 模块控制
不适用:
↓
用户权限判断
核心理解
👉 @Conditional 不是控制"执行不执行"
👉 而是控制:
这个 Bean 在系统里"有没有"
下一篇:
AOP + 日志 + traceId 完整链路追踪(企业级模板)
内容包括:
- 请求唯一 traceId
- AOP 自动打印日志
- 日志贯穿 Controller → Service → DB
- 出问题一条链路查到底