Spring Boot条件注解详解

0 阅读13分钟

 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 注解分类速查表

分类注解作用
类条件@ConditionalOnClassclasspath中存在指定类时生效
@ConditionalOnMissingClassclasspath中不存在指定类时生效
Bean条件@ConditionalOnBean容器中存在指定Bean时生效
@ConditionalOnMissingBean容器中不存在指定Bean时生效
属性条件@ConditionalOnProperty配置属性满足条件时生效
应用条件@ConditionalOnWebApplication是Web应用时生效
@ConditionalOnNotWebApplication不是Web应用时生效
资源条件@ConditionalOnResource存在指定资源文件时生效
表达式条件@ConditionalOnExpressionSpEL表达式为true时生效
其他条件@ConditionalOnJndiJNDI存在时生效
@ConditionalOnJavaJava版本满足条件时生效
@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_6VERSION_1_7VERSION_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();
    }
}

支持的云平台:

平台枚举值
KubernetesCloudPlatform.KUBERNETES
Cloud FoundryCloudPlatform.CLOUD_FOUNDRY
HerokuCloudPlatform.HEROKU
SAPCloudPlatform.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等注解全解析