Spring Boot条件注解详解
一、什么是条件注解?
条件注解(Conditional Annotations)是Spring框架提供的一种机制,它允许根据特定条件决定是否注册Bean或执行配置。在Spring Boot中,条件注解是实现自动装配的核心技术之一。通过这些条件注解,Spring Boot可以实现"按需加载",避免不必要的Bean初始化。
二、条件注解家族概览
2.1 核心注解层次结构
类条件注解 :
@ConditionalOnClass- classpath中存在指定类时生效@ConditionalOnMissingClass- classpath中不存在指定类时生效
Bean条件注解 :
@ConditionalOnBean- 容器中存在指定Bean时生效@ConditionalOnMissingBean- 容器中不存在指定Bean时生效
属性条件注解 :
@ConditionalOnProperty- 配置属性满足条件时生效
应用类型条件注解 :
@ConditionalOnWebApplication- 是Web应用时生效@ConditionalOnNotWebApplication- 不是Web应用时生效
其他条件注解 :
@ConditionalOnResource- 存在指定资源文件时生效@ConditionalOnExpression- SpEL表达式为true时生效@ConditionalOnJava- Java版本满足条件时生效@ConditionalOnJndi- JNDI存在时生效@ConditionalOnSingleCandidate- 容器中只有一个指定Bean时生效
2.2 注解分类速查表
| 分类 | 注解 | 作用 |
|---|---|---|
| 类条件 | @ConditionalOnClass | classpath中存在指定类时生效 |
@ConditionalOnMissingClass | classpath中不存在指定类时生效 | |
| Bean条件 | @ConditionalOnBean | 容器中存在指定Bean时生效 |
@ConditionalOnMissingBean | 容器中不存在指定Bean时生效 | |
| 属性条件 | @ConditionalOnProperty | 配置属性满足条件时生效 |
| 应用条件 | @ConditionalOnWebApplication | 是Web应用时生效 |
@ConditionalOnNotWebApplication | 不是Web应用时生效 | |
| 资源条件 | @ConditionalOnResource | 存在指定资源文件时生效 |
| 表达式条件 | @ConditionalOnExpression | SpEL表达式为true时生效 |
| 其他条件 | @ConditionalOnJndi | JNDI存在时生效 |
@ConditionalOnJava | Java版本满足条件时生效 | |
@ConditionalOnSingleCandidate | 容器中只有一个指定Bean时生效 |
三、@Conditional:条件注解的鼻祖
3.1 基本用法
@Conditional是所有条件注解的基础,它接受一个实现了 Condition接口的类:
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
3.2 自定义条件示例
public class LinuxCondition implements Condition {
// Linux条件判断
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return osName != null && osName.toLowerCase().contains("linux");
}
}
public class WindowsCondition implements Condition {
// Windows条件判断
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return osName != null && osName.toLowerCase().contains("windows");
}
}
使用自定义条件:
@Configuration
public class OSConfig {
// Linux服务
@Bean
@Conditional(LinuxCondition.class)
public CommandService linuxCommandService() {
return new LinuxCommandService();
}
// Windows服务
@Bean
@Conditional(WindowsCondition.class)
public CommandService windowsCommandService() {
return new WindowsCommandService();
}
}
3.3 ConditionContext详解
ConditionContext提供了丰富的上下文信息:
public interface ConditionContext {
// Bean定义注册器
BeanDefinitionRegistry getRegistry();
// Bean工厂
ConfigurableListableBeanFactory getBeanFactory();
// 环境信息
Environment getEnvironment();
// 资源加载器
ResourceLoader getResourceLoader();
// 类加载器
ClassLoader getClassLoader();
}
| 方法 | 作用 |
|---|---|
getRegistry() | 获取Bean定义注册器,可检查Bean定义是否存在 |
getBeanFactory() | 获取Bean工厂,可检查Bean是否存在、获取Bean |
getEnvironment() | 获取环境信息,可读取配置属性 |
getResourceLoader() | 获取资源加载器,可加载资源文件 |
getClassLoader() | 获取类加载器,可检查类是否存在 |
四、类条件注解详解
4.1 @ConditionalOnClass
当classpath中存在指定类时,配置才生效:
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
// 自动配置数据源
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
源码解析 :
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface OnClassCondition {
Class<?>[] value() default {};
String[] name() default {};
}
使用场景 :
- 自动配置类依赖特定类时
- 根据引入的依赖决定是否启用功能
4.2 @ConditionalOnMissingClass
当classpath中不存在指定类时,配置才生效:
@Configuration
@ConditionalOnMissingClass("com.fasterxml.jackson.databind.ObjectMapper")
public class GsonAutoConfiguration {
// 当Jackson不存在时,使用Gson
@Bean
@ConditionalOnMissingBean
public Gson gson() {
return new Gson();
}
}
4.3 实战案例:多JSON库支持
@Configuration
public class JsonConfig {
// Jackson JSON
@Bean
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnMissingBean
public JsonService jacksonJsonService() {
return new JacksonJsonService();
}
// Gson JSON
@Bean
@ConditionalOnClass(Gson.class)
@ConditionalOnMissingBean(JsonService.class)
public JsonService gsonJsonService() {
return new GsonJsonService();
}
// 默认JSON
@Bean
@ConditionalOnMissingBean(JsonService.class)
public JsonService defaultJsonService() {
return new SimpleJsonService();
}
}
五、Bean条件注解详解
5.1 @ConditionalOnBean
当容器中存在指定Bean时,配置才生效:
@Configuration
public class RedisConfig {
// 当DataSource存在时,创建Redis缓存管理器
@Bean
@ConditionalOnBean(DataSource.class)
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory).build();
}
}
注意事项 :
@Configuration
public class ExampleConfig {
// ServiceA
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
// 依赖ServiceA的ServiceB
@Bean
@ConditionalOnBean(ServiceA.class)
public ServiceB serviceB() {
return new ServiceB();
}
}
⚠️ 重要提示 :
@ConditionalOnBean在同一个配置类中使用时,由于Bean的注册顺序问题,可能会导致条件判断失败。建议将条件Bean放在不同的配置类中(详见5.3节)。
5.2 @ConditionalOnMissingBean
当容器中不存在指定Bean时,配置才生效。这是实现"允许用户覆盖默认配置"的关键:
@Configuration
public class WebMvcAutoConfiguration {
// 默认视图解析器
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
用户可以自定义覆盖:
@Configuration
public class MyWebConfig {
// 自定义视图解析器
@Bean
public InternalResourceViewResolver myViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
return resolver;
}
}
5.3 Bean条件的陷阱
问题场景 :
@Configuration
public class ProblematicConfig {
// 问题:同配置类中使用@ConditionalOnBean可能失败
@Bean
@ConditionalOnMissingBean
public ServiceA serviceA() {
return new ServiceA();
}
// 可能失败!因为ServiceA可能还未注册
@Bean
@ConditionalOnBean(ServiceA.class)
public ServiceB serviceB() {
return new ServiceB();
}
}
解决方案 :
// 分离到不同的配置类
@Configuration
public class ServiceAConfig {
// 先定义ServiceA
@Bean
@ConditionalOnMissingBean
public ServiceA serviceA() {
return new ServiceA();
}
}
@Configuration
@ConditionalOnBean(ServiceA.class)
public class ServiceBConfig {
// ServiceA存在时才创建ServiceB
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
5.4 指定Bean的多种方式
@Configuration
public class BeanConditionConfig {
// 按Bean名称匹配
@Bean
@ConditionalOnBean(name = "dataSource")
public ServiceA serviceA() {
return new ServiceA();
}
// 按类型匹配
@Bean
@ConditionalOnBean(type = "com.example.DataSource")
public ServiceB serviceB() {
return new ServiceB();
}
// 排除特定类型
@Bean
@ConditionalOnBean(value = DataSource.class,
ignored = HikariDataSource.class)
public ServiceC serviceC() {
return new ServiceC();
}
// 按注解匹配
@Bean
@ConditionalOnBean(annotation = EnableScheduling.class)
public ServiceD serviceD() {
return new ServiceD();
}
}
5.5 Bean搜索策略
@ConditionalOnBean 和 @ConditionalOnMissingBean 都支持 search 属性,用于指定Bean的搜索策略:
@Configuration
public class SearchStrategyConfig {
// 搜索所有容器(包括父容器)
@Bean
@ConditionalOnBean(name = "dataSource", search = SearchStrategy.ALL)
public ServiceA serviceA() {
return new ServiceA();
}
// 只搜索当前容器,不包括父容器
@Bean
@ConditionalOnBean(name = "dataSource", search = SearchStrategy.CURRENT)
public ServiceB serviceB() {
return new ServiceB();
}
// 不执行搜索,仅检查是否已注册
@Bean
@ConditionalOnBean(name = "dataSource", search = SearchStrategy.NONE)
public ServiceC serviceC() {
return new ServiceC();
}
}
| 搜索策略 | 说明 |
|---|---|
SearchStrategy.ALL | 在当前容器和所有父容器中搜索(默认) |
SearchStrategy.CURRENT | 只在当前容器中搜索 |
SearchStrategy.NONE | 不执行搜索,仅检查已注册的Bean |
使用场景 :在父子容器场景(如Spring MVC与Spring Boot集成)中使用
SearchStrategy.CURRENT可以避免父容器中的Bean被意外匹配。
六、属性条件注解详解
6.1 @ConditionalOnProperty基本用法
@Configuration
public class FeatureConfig {
// 根据配置属性决定是否创建Bean
@Bean
@ConditionalOnProperty(name = "feature.logging.enabled", havingValue = "true")
public LoggingService loggingService() {
return new LoggingService();
}
}
6.2 参数详解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
// 属性名称
String[] value() default {};
// 属性前缀
String prefix() default "";
// 属性名称(可多个)
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}
| 参数 | 说明 |
|---|---|
value/name | 属性名称,可以指定一个或多个 |
prefix | 属性前缀,拼接在name前面 |
havingValue | 期望的属性值,当属性等于该值时匹配 |
matchIfMissing | 属性不存在时是否匹配,默认为false |
6.3 各种使用场景
场景一:属性值等于特定值
@Bean
@ConditionalOnProperty(name = "app.mode", havingValue = "production")
public ProductionService productionService() {
return new ProductionService();
}
场景二:属性存在即生效
@Bean
@ConditionalOnProperty(name = "app.feature.enabled")
public FeatureService featureService() {
return new FeatureService();
}
场景三:属性不存在时默认生效
@Bean
@ConditionalOnProperty(name = "app.cache.enabled",
havingValue = "true",
matchIfMissing = true)
public CacheService cacheService() {
return new CacheService();
}
场景四:使用前缀
@Configuration
@ConditionalOnProperty(prefix = "app.database", name = "type", havingValue = "mysql")
public class MySqlConfig {
// MySQL数据源配置
@Bean
public DataSource dataSource() {
return new MySqlDataSource();
}
}
场景五:多属性组合
// 多个属性同时满足才生效
@Bean
@ConditionalOnProperty(name = {"app.feature.enabled", "app.feature.licensed"},
havingValue = "true")
public LicensedFeatureService licensedFeatureService() {
return new LicensedFeatureService();
}
6.4 实战案例:功能开关
@Configuration
public class FeatureToggleConfig {
// 新UI服务 - 启用时
@Bean
@ConditionalOnProperty(prefix = "feature.new-ui",
name = "enabled",
havingValue = "true")
public NewUIService newUIService() {
return new NewUIService();
}
// 旧UI服务 - 未启用或默认
@Bean
@ConditionalOnProperty(prefix = "feature.new-ui",
name = "enabled",
havingValue = "false",
matchIfMissing = true)
public LegacyUIService legacyUIService() {
return new LegacyUIService();
}
// 限流过滤器
@Bean
@ConditionalOnProperty(prefix = "feature.rate-limit",
name = "enabled",
havingValue = "true")
public RateLimitFilter rateLimitFilter() {
return new RateLimitFilter();
}
}
配置文件:
feature:
new-ui:
enabled: true
rate-limit:
enabled: false
七、应用类型条件注解详解
7.1 @ConditionalOnWebApplication
当应用是Web应用时生效:
@Configuration
@ConditionalOnWebApplication
public class WebConfig {
// CORS配置
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
};
}
}
7.2 指定Web应用类型
// Servlet Web应用
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
public class ServletWebConfig {
}
// Reactive Web应用
@Configuration
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class ReactiveWebConfig {
}
7.3 @ConditionalOnNotWebApplication
当应用不是Web应用时生效:
@Configuration
@ConditionalOnNotWebApplication
public class NonWebConfig {
// 非Web应用的命令行 runner
@Bean
public CommandLineRunner commandLineRunner() {
return args -> {
System.out.println("Running in non-web mode");
};
}
}
八、资源条件注解详解
8.1 @ConditionalOnResource
当存在指定资源文件时生效:
@Configuration
@ConditionalOnResource(resources = "classpath:custom-config.properties")
public class CustomConfig {
// 自定义配置服务
@Bean
public CustomService customService() {
return new CustomService();
}
}
8.2 多资源检查
@Configuration
@ConditionalOnResource(resources = {
"classpath:db-config.properties",
"classpath:redis-config.properties"
})
public class MultiResourceConfig {
// 多资源服务
@Bean
public MultiResourceService multiResourceService() {
return new MultiResourceService();
}
}
8.3 实战案例:基于配置文件初始化
@Configuration
public class DataSourceConfig {
@Bean
@ConditionalOnResource(resources = "classpath:db.properties")
@ConditionalOnMissingBean
public DataSource fileBasedDataSource() {
Properties props = loadProperties("db.properties");
return createDataSource(props);
}
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource defaultDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
九、表达式条件注解详解
9.1 @ConditionalOnExpression
使用SpEL表达式进行复杂条件判断:
@Configuration
public class ExpressionConfig {
@Bean
@ConditionalOnExpression("${app.feature.enabled} and ${app.feature.licensed}")
public LicensedFeatureService licensedFeatureService() {
return new LicensedFeatureService();
}
@Bean
@ConditionalOnExpression("#{systemProperties['os.name'].toLowerCase().contains('linux')}")
public LinuxService linuxService() {
return new LinuxService();
}
@Bean
@ConditionalOnExpression("#{environment['app.mode'] == 'production' and environment['app.cluster.enabled'] == 'true'}")
public ClusterService clusterService() {
return new ClusterService();
}
}
9.2 复杂表达式示例
@Configuration
public class ComplexExpressionConfig {
@Bean
@ConditionalOnExpression("#{T(java.lang.Runtime).getRuntime().availableProcessors() >= 4}")
public MultiThreadService multiThreadService() {
return new MultiThreadService();
}
@Bean
@ConditionalOnExpression("#{systemEnvironment['ENV_TYPE'] != null and systemEnvironment['ENV_TYPE'].equals('PROD')}")
public ProductionService productionService() {
return new ProductionService();
}
@Bean
@ConditionalOnExpression("#{@userService.isAdmin()}")
public AdminService adminService() {
return new AdminService();
}
}
十、其他条件注解详解
10.1 @ConditionalOnJava
根据Java版本判断:
@Configuration
public class JavaVersionConfig {
@Bean
@ConditionalOnJava(JavaVersion.VERSION_8)
public Java8Service java8Service() {
return new Java8Service();
}
@Bean
@ConditionalOnJava(value = JavaVersion.VERSION_17, range = Range.EQUAL_OR_NEWER)
public ModernJavaService modernJavaService() {
return new ModernJavaService();
}
}
支持的版本枚举:VERSION_1_6、VERSION_1_7、VERSION_1_8(或 VERSION_8)、VERSION_9及以上等。
10.2 @ConditionalOnJndi
根据JNDI环境判断:
@Configuration
@ConditionalOnJndi("java:comp/env/jdbc/myDataSource")
public class JndiConfig {
@Bean
public DataSource jndiDataSource() {
JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName("java:comp/env/jdbc/myDataSource");
return (DataSource) factory.getObject();
}
}
10.3 @ConditionalOnSingleCandidate
当容器中只有一个指定类型的Bean,或者有多个但有一个标记为@Primary时生效:
@Configuration
public class SingleCandidateConfig {
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
10.4 @ConditionalOnCloudPlatform
根据云平台环境判断:
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesConfig {
@Bean
public KubernetesService kubernetesService() {
return new KubernetesService();
}
}
支持的云平台:
| 平台 | 枚举值 |
|---|---|
| Kubernetes | CloudPlatform.KUBERNETES |
| Cloud Foundry | CloudPlatform.CLOUD_FOUNDRY |
| Heroku | CloudPlatform.HEROKU |
| SAP | CloudPlatform.SAP |
十一、组合条件注解
11.1 AND关系(默认)
多个条件注解同时使用时,默认是AND关系:
@Bean
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.enabled", havingValue = "true")
public DataSourceService dataSourceService() {
return new DataSourceService();
}
11.2 OR关系(自定义实现)
Spring Boot没有直接提供OR关系的条件注解,需要自定义(基于3.1节介绍的Condition接口):
public class OnAnyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(
ConditionalOnAny.class.getName());
Class<? extends Condition>[] conditions =
(Class<? extends Condition>[]) attributes.get("value");
for (Class<? extends Condition> conditionClass : conditions) {
try {
Condition condition = conditionClass.newInstance();
if (condition.matches(context, metadata)) {
return true;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return false;
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnAnyCondition.class)
public @interface ConditionalOnAny {
Class<? extends Condition>[] value();
}
使用示例:
@Bean
@ConditionalOnAny({LinuxCondition.class, MacCondition.class})
public UnixLikeService unixLikeService() {
return new UnixLikeService();
}
11.3 复杂组合示例
@Configuration
public class ComplexConditionConfig {
@Bean
@ConditionalOnWebApplication
@ConditionalOnClass({RedisTemplate.class, StringRedisTemplate.class})
@ConditionalOnProperty(prefix = "spring.redis", name = "enabled",
havingValue = "true", matchIfMissing = true)
@ConditionalOnMissingBean
public RedisCacheService redisCacheService() {
return new RedisCacheService();
}
}
十二、条件注解执行顺序
12.1 ConfigurationCondition接口
通过实现 ConfigurationCondition可以控制条件的执行时机:
public interface ConfigurationCondition extends Condition {
ConfigurationPhase getConfigurationPhase();
enum ConfigurationPhase {
PARSE_CONFIGURATION,
REGISTER_BEAN
}
}
| 阶段 | 说明 |
|---|---|
PARSE_CONFIGURATION | 在解析配置类时判断,决定是否解析整个配置类 |
REGISTER_BEAN | 在注册Bean时判断,决定是否注册单个Bean |
12.2 自定义执行时机
public class MyParseCondition implements ConfigurationCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment()
.getProperty("app.config.enabled", Boolean.class, true);
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
}
12.3 条件注解执行流程
条件注解的执行分为两个阶段:
阶段一:解析配置类阶段 (PARSE_CONFIGURATION)
- 检查类级别的条件注解(如
@ConditionalOnClass、@ConditionalOnMissingClass) - 如果条件不满足,跳过整个配置类
阶段二:注册Bean阶段 (REGISTER_BEAN)
- 检查方法级别的条件注解(如
@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnProperty) - 如果条件不满足,跳过该Bean的注册
十三、调试条件注解
13.1 启用条件评估报告
debug=true
启动后查看控制台输出:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
- @ConditionalOnClass found required classes 'org.springframework.aop.aspectj.AspectJExpressionPointcut'
and 'org.springframework.aop.framework.ProxyFactoryBean' (OnClassCondition)
Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
13.2 使用ConditionEvaluationReport
@Component
public class ConditionReporter implements ApplicationRunner {
@Autowired
private ConfigurableApplicationContext context;
@Override
public void run(ApplicationArguments args) {
ConditionEvaluationReport report = ConditionEvaluationReport
.get(context.getBeanFactory());
System.out.println("=== 条件评估报告 ===");
System.out.println("匹配的配置:");
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
if (outcomes.isFullMatch()) {
System.out.println(" ✓ " + source);
}
});
System.out.println("未匹配的配置:");
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
if (!outcomes.isFullMatch()) {
System.out.println(" ✗ " + source);
outcomes.forEach(outcome -> {
if (!outcome.getOutcome().isMatch()) {
System.out.println(" - " + outcome.getOutcome().getMessage());
}
});
}
});
}
}
13.3 Actuator端点
management.endpoints.web.exposure.include=conditions
访问 http://localhost:8080/actuator/conditions 查看详细的条件评估报告。
十四、最佳实践
14.1 条件注解使用原则
| 原则 | 说明 |
|---|---|
| 单一职责 | 每个条件只检查一个维度 |
| 明确语义 | 条件判断逻辑要清晰易懂 |
| 避免循环依赖 | 条件之间不要相互依赖 |
| 提供默认值 | 使用 matchIfMissing提供合理默认 |
| 文档化 | 复杂条件要添加注释说明 |
14.2 常见反模式
// ❌ 反模式:在同一个配置类中使用@ConditionalOnBean依赖同类的其他Bean
@Configuration
public class BadConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
@ConditionalOnBean(ServiceA.class) // 可能失败!
public ServiceB serviceB() {
return new ServiceB();
}
}
// ✅ 正确做法:分离配置类
@Configuration
class ServiceAConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
}
@Configuration
@ConditionalOnBean(ServiceA.class)
class ServiceBConfig {
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
// ❌ 反模式:过度复杂的条件表达式
@Bean
@ConditionalOnExpression("#{environment['app.mode'] == 'prod' and " +
"environment['app.region'] == 'cn' and " +
"T(java.lang.Runtime).getRuntime().availableProcessors() >= 4}")
public ComplexService complexService() {
return new ComplexService();
}
// ✅ 正确做法:拆分为多个条件或自定义Condition
@Bean
@Conditional(ProductionChinaCondition.class)
public ComplexService complexService() {
return new ComplexService();
}
14.3 自动配置类模板
以下是一个完整的自动配置类示例,展示了常用条件注解的组合使用:
// @AutoConfiguration - 自动配置注解
// @ConditionalOnClass - 详见4.1节
// @EnableConfigurationProperties - 配置属性绑定
@AutoConfiguration
@ConditionalOnClass(ExampleService.class)
@EnableConfigurationProperties(ExampleProperties.class)
public class ExampleAutoConfiguration {
private final ExampleProperties properties;
public ExampleAutoConfiguration(ExampleProperties properties) {
this.properties = properties;
}
// @ConditionalOnMissingBean - 详见5.2节
// @ConditionalOnProperty - 详见第六章
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example", name = "enabled",
havingValue = "true", matchIfMissing = true)
public ExampleService exampleService() {
return new ExampleService(properties);
}
// @ConditionalOnBean - 详见5.1节
@Bean
@ConditionalOnBean(ExampleService.class)
@ConditionalOnProperty(prefix = "example.cache", name = "enabled",
havingValue = "true")
public ExampleCache exampleCache(ExampleService service) {
return new ExampleCache(service);
}
}
十五、总结
15.1 条件注解选择指南
需要判断什么条件时,选择对应的注解:
| 条件类型 | 注解 |
|---|---|
| 类是否存在 | @ConditionalOnClass、@ConditionalOnMissingClass |
| Bean是否存在 | @ConditionalOnBean、@ConditionalOnMissingBean |
| 配置属性 | @ConditionalOnProperty |
| 是否Web应用 | @ConditionalOnWebApplication、@ConditionalOnNotWebApplication |
| 资源文件 | @ConditionalOnResource |
| 复杂表达式 | @ConditionalOnExpression |
| Java版本 | @ConditionalOnJava |
| 云平台 | @ConditionalOnCloudPlatform |
| 自定义条件 | @Conditional + 实现Condition接口 |
15.2 核心要点回顾
| 要点 | 内容 |
|---|---|
| 基础注解 | @Conditional是所有条件注解的基础(详见第三章) |
| 类条件 | 在解析配置类阶段执行,影响整个配置类(详见第四章) |
| Bean条件 | 在注册Bean阶段执行,注意同配置类的顺序问题(详见第五章) |
| 属性条件 | 使用 matchIfMissing提供合理默认值(详见第六章) |
| 组合使用 | 多个条件默认是AND关系(详见第十一章) |
| 调试 | 开启 debug=true查看条件评估报告(详见第十三章) |
条件注解是Spring Boot自动装配的核心机制,掌握它能帮助我们:
- 理解Spring Boot自动配置原理
- 编写可扩展的自动配置模块
- 实现灵活的功能开关
- 构建条件化的应用逻辑
参考资料:
Spring Boot条件注解详解 | @ConditionalOnBean、@ConditionalOnProperty等注解全解析