Spring中@Conditional注解详解:条件装配的终极指南
一、为什么要用条件装配?
在实际开发中,我们经常需要根据不同的运行环境、配置参数或依赖情况动态决定是否注册某个Bean。例如:
-
开发环境使用内存数据库,生产环境连接真实数据库
-
当存在某个类时才启用特定功能
-
根据配置文件开关决定是否初始化组件
这时就需要用到Spring的条件装配机制,而@Conditional
注解正是实现这一功能的核心!
二、@Conditional注解简介
1. 核心作用
@Conditional
是Spring 4.0引入的条件化Bean注册注解,通过它可以实现:
if(满足指定条件) {
注册当前Bean
}
2. 核心接口
其底层依赖Condition
接口:
public interface Condition {
boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata);
}
3. 常见派生注解
Spring Boot基于它扩展了:
-
@ConditionalOnProperty
-
@ConditionalOnClass
-
@ConditionalOnMissingBean
-
等20+条件注解
三、使用方式(三步走)
步骤1:自定义条件类
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// 返回true则创建Bean
return checkSomeCondition();
}
}
步骤2:标注条件注解
@Configuration
public class AppConfig {
@Bean
@Conditional(MyCondition.class) // 关键注解
public MyService myService() {
return new MyService();
}
}
步骤3:验证结果
当MyCondition.matches()
返回true
时,MyService
会被注册到容器,否则忽略。
四、实战案例:多环境配置
场景描述
根据不同的运行环境(dev/test/prod)加载不同的数据源配置。
1. 定义环境条件
public class EnvCondition implements Condition {
@Override
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// 获取环境变量
String env = context.getEnvironment()
.getProperty("app.env", "dev");
// 获取注解中定义的目标环境
Map<String, Object> attrs = metadata.getAnnotationAttributes(
ConditionalOnEnv.class.getName());
String targetEnv = (String) attrs.get("value");
return targetEnv.equalsIgnoreCase(env);
}
}
// 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(EnvCondition.class)
public @interface ConditionalOnEnv {
String value();
}
2. 配置不同环境Bean
@Configuration
public class DataSourceConfig {
// 开发环境数据源
@Bean
@ConditionalOnEnv("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
// 生产环境数据源
@Bean
@ConditionalOnEnv("prod")
public DataSource prodDataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://prod-server:3306/db");
// 其他配置...
return ds;
}
}
3. 测试验证
// 设置环境变量
System.setProperty("app.env", "prod");
ApplicationContext ctx = new AnnotationConfigApplicationContext(
AppConfig.class);
// 生产数据源将被注入
DataSource dataSource = ctx.getBean(DataSource.class);
五、高级用法:组合条件
多个条件可以通过AllNestedConditions
实现逻辑与:
public class AllConditions extends AllNestedConditions {
public AllConditions() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(DataSource.class)
static class OnClassCondition {}
@ConditionalOnProperty("spring.datasource.url")
static class OnPropertyCondition {}
}
// 使用组合条件
@Configuration
@Conditional(AllConditions.class)
public class DataSourceAutoConfiguration {
// 当类路径存在DataSource且配置了url时生效
}
六、注意事项
-
条件判断时机:发生在Bean定义阶段,早于Bean实例化
-
优先级问题:
@Conditional
的优先级高于@Profile
-
避免循环依赖:条件判断中不要直接获取Bean实例
-
调试技巧:通过
-Ddebug
查看条件评估报告(Spring Boot)
七、总结
@Conditional
为Spring应用提供了灵活的条件装配能力,通过本文你可以:
✅ 掌握自定义条件装配的实现方法
✅ 了解多环境配置的最佳实践
✅ 学会处理复杂条件组合场景