百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

SpringBoot条件化配置(@Conditional)全面解析与实战指南

mhr18 2025-06-12 14:36 5 浏览 0 评论

一、条件化配置基础概念

1.1 什么是条件化配置

条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在Spring Boot中,这一机制通过@Conditional注解及其衍生注解得到了极大的增强和广泛应用。

通俗理解:想象你是一家餐厅的经理,你需要根据不同的情况来决定是否提供某些菜品:

  • 如果是夏天,就提供冰镇饮品
  • 如果是冬天,就提供热饮
  • 如果食材库存不足,就从菜单中暂时下架某些菜品

Spring的条件化配置就是帮你实现这种"智能判断"的编程机制。

1.2 @Conditional注解的核心作用

@Conditional注解允许开发者在Spring容器注册Bean时,根据特定条件进行动态决策。这种机制使得应用程序能够根据运行环境、配置属性、类路径情况等因素自动调整其行为。

核心优势:

  1. 环境适配:根据不同的环境(dev/test/prod)加载不同的配置
  2. 功能开关:通过配置参数控制某些功能的开启与关闭
  3. 自动配置:Spring Boot自动配置的核心机制
  4. 资源优化:避免加载当前环境不需要的组件,节省资源

1.3 条件化配置的基本语法

@Configuration
public class MyConfiguration {
    
    @Bean
    @Conditional(MyCondition.class)
    public MyService myService() {
        return new MyServiceImpl();
    }
}

其中MyCondition需要实现Condition接口:

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 判断逻辑
        return true; // 或false
    }
}

1.4 条件化配置的核心接口与类

类/接口

作用

重要方法

@Conditional

元注解,用于声明条件

value() - 指定Condition类

Condition

条件判断接口

matches() - 核心判断方法

ConditionContext

条件判断上下文

getEnvironment(), getBeanFactory()等

AnnotatedTypeMetadata

注解元数据

getAnnotationAttributes()等

二、Spring Boot中的条件注解

2.1 Spring Boot对@Conditional的扩展

Spring Boot在基础@Conditional注解之上,提供了一系列特定场景的条件注解,使开发更加便捷:

注解

作用

等效条件

@ConditionalOnBean

当容器中存在指定Bean时生效

基于Bean存在的条件

@ConditionalOnMissingBean

当容器中不存在指定Bean时生效

基于Bean不存在的条件

@ConditionalOnClass

当类路径中存在指定类时生效

基于类存在的条件

@ConditionalOnMissingClass

当类路径中不存在指定类时生效

基于类不存在的条件

@ConditionalOnProperty

当配置属性满足条件时生效

基于配置属性的条件

@ConditionalOnResource

当指定资源存在时生效

基于资源存在的条件

@ConditionalOnWebApplication

当是Web应用时生效

基于应用类型的条件

@ConditionalOnNotWebApplication

当不是Web应用时生效

基于应用类型的条件

@ConditionalOnExpression

当SpEL表达式为true时生效

基于表达式的条件

@ConditionalOnJava

当Java版本符合要求时生效

基于Java版本的条件

2.2 常用条件注解详解

2.2.1 @ConditionalOnProperty

这是最常用的条件注解之一,它根据配置属性的值来决定是否创建Bean。

@Configuration
public class MyConfig {
    
    @Bean
    @ConditionalOnProperty(
        prefix = "app.feature",
        name = "enabled",
        havingValue = "true",
        matchIfMissing = false
    )
    public FeatureService featureService() {
        return new FeatureServiceImpl();
    }
}

参数说明:

  • prefix: 配置属性的前缀
  • name: 属性名称(可与prefix组合成完整属性名)
  • havingValue: 匹配的值
  • matchIfMissing: 当属性缺失时是否匹配(默认false)

2.2.2 @ConditionalOnBean与@ConditionalOnMissingBean

这对注解用于基于Bean存在与否的条件判断。

@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        // 默认数据源配置
        return new HikariDataSource();
    }
    
    @Bean
    @ConditionalOnBean(DataSource.class)
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        // 只有在DataSource存在时才创建JdbcTemplate
        return new JdbcTemplate(dataSource);
    }
}

2.2.3 @ConditionalOnClass与@ConditionalOnMissingClass

这对注解根据类路径中类的存在与否进行条件判断。

@Configuration
@ConditionalOnClass(RedisClient.class)
public class RedisAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingClass("com.oracle.OracleDriver")
    public RedisTemplate redisTemplate() {
        // 当RedisClient存在且Oracle驱动不存在时配置Redis
        return new RedisTemplate();
    }
}

2.3 条件注解的组合使用

多个条件注解可以通过逻辑与的关系组合使用:

@Configuration
@ConditionalOnClass(RedisClient.class)
@ConditionalOnProperty(prefix = "app.redis", name = "enabled", havingValue = "true")
public class RedisConfig {
    // 当RedisClient存在且app.redis.enabled=true时生效
}

如果需要更复杂的逻辑,可以使用@ConditionalOnExpression

@Bean
@ConditionalOnExpression(
    "@environment.getProperty('app.feature.enabled') == 'true' " +
    "and ${app.feature.priority} > 3"
)
public AdvancedFeature advancedFeature() {
    // 使用SpEL表达式定义复杂条件
}

三、条件化配置的实现原理

3.1 Spring条件化配置的处理流程

Spring处理条件化配置的核心流程如下:

ConditionConditionEvaluatorConfigurationClassParserConditionConditionEvaluatorConfigurationClassParser解析配置类时请求条件评估调用matches方法返回boolean结果返回是否应该跳过

详细步骤解析:

  1. 在Spring容器启动过程中,ConfigurationClassPostProcessor会处理所有@Configuration
  2. 对于每个配置类和其中的每个@Bean方法,都会通过ConditionEvaluator进行条件评估
  3. ConditionEvaluator会检查相关的@Conditional注解,并调用相应Condition实现的matches方法
  4. 如果所有条件都满足,则注册该Bean;否则跳过

3.2 Condition接口的深入分析

Condition接口是条件化配置的核心:

public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • ConditionContext: 提供评估条件时所需的上下文信息
    • getRegistry(): 返回BeanDefinition注册表
    • getBeanFactory(): 返回ConfigurableListableBeanFactory
    • getEnvironment(): 返回Environment对象
    • getResourceLoader(): 返回ResourceLoader
    • getClassLoader(): 返回ClassLoader
  • AnnotatedTypeMetadata: 提供对被注解元素的访问
    • getAnnotationAttributes(): 获取注解属性
    • isAnnotated(): 检查是否带有特定注解

3.3 条件评估的缓存机制

Spring会对条件评估结果进行缓存以提高性能:

  1. 在配置类解析阶段,ConfigurationClassParser会维护一个ConditionEvaluator实例
  2. 对于每个配置类和方法,条件评估结果会被缓存
  3. 相同的条件在相同上下文中不会重复评估

这种机制确保了条件判断的高效性,特别是在大型应用中可能有数百个条件需要评估时。

四、自定义条件实现

4.1 实现自定义Condition

当Spring Boot提供的条件注解不能满足需求时,我们可以自定义Condition实现。

案例:实现一个只在工作日生效的条件

public class WorkdayCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取当前日期
        DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
        // 周一至周五是工作日
        return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
    }
}

使用自定义条件:

@Configuration
public class WorkdayConfig {
    
    @Bean
    @Conditional(WorkdayCondition.class)
    public WorkdayService workdayService() {
        return new WorkdayServiceImpl();
    }
}

4.2 带参数的自定义条件

我们可以通过自定义注解来使条件更加灵活和可配置。

  1. 首先定义自定义注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnWorkdayCondition.class)
public @interface ConditionalOnWorkday {
    
    /**
     * 是否包含周五
     */
    boolean includeFriday() default true;
}
  1. 然后实现对应的Condition:
public class OnWorkdayCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取注解属性
        Map<String, Object> attributes = metadata
            .getAnnotationAttributes(ConditionalOnWorkday.class.getName());
        boolean includeFriday = (boolean) attributes.get("includeFriday");
        
        DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
        
        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
            return false;
        }
        
        if (!includeFriday && dayOfWeek == DayOfWeek.FRIDAY) {
            return false;
        }
        
        return true;
    }
}
  1. 使用带参数的条件注解:
@Configuration
public class SpecialConfig {
    
    @Bean
    @ConditionalOnWorkday(includeFriday = false)
    public SpecialService specialService() {
        // 只在周一至周四生效
        return new SpecialServiceImpl();
    }
}

4.3 条件实现的注意事项

  1. 性能考虑:条件判断可能会在应用启动时执行多次,应确保逻辑高效
  2. 无状态性:Condition实现应该是无状态的,不应依赖实例变量
  3. 线程安全:matches方法可能会被多个线程同时调用,必须保证线程安全
  4. 避免复杂逻辑:尽量保持条件判断简单,复杂逻辑考虑拆分为多个条件
  5. 合理缓存:对于耗时的判断,可考虑在Condition实现内部进行适当缓存

五、条件化配置的实战应用

5.1 多环境配置管理

条件化配置非常适合用于管理不同环境的配置差异。

@Configuration
public class EnvConfig {
    
    @Bean
    @Profile("dev")  // 等同于@ConditionalOnProperty(name="spring.profiles.active", havingValue="dev")
    public DataSource devDataSource() {
        // 开发环境数据源
        return DataSourceBuilder.create()
            .url("jdbc:h2:mem:dev")
            .username("sa")
            .password("")
            .build();
    }
    
    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        // 生产环境数据源
        return DataSourceBuilder.create()
            .url("jdbc:mysql://prod-db:3306/app")
            .username("prod-user")
            .password("secure-password")
            .build();
    }
    
    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource defaultDataSource() {
        // 默认数据源
        return DataSourceBuilder.create()
            .url("jdbc:h2:mem:default")
            .username("sa")
            .password("")
            .build();
    }
}

5.2 功能开关实现

使用条件化配置实现功能开关(Feature Toggle):

@Configuration
public class FeatureConfig {
    
    @Bean
    @ConditionalOnProperty(name = "features.advanced.enabled", havingValue = "true")
    public AdvancedFeature advancedFeature() {
        return new AdvancedFeatureImpl();
    }
    
    @Bean
    @ConditionalOnExpression("${features.experimental.enabled:false} && ${features.experimental.group} == 'beta'")
    public ExperimentalFeature experimentalFeature() {
        return new ExperimentalFeatureImpl();
    }
    
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
    public KubernetesFeature kubernetesFeature() {
        return new KubernetesFeatureImpl();
    }
}

5.3 自动配置类中的条件化使用

Spring Boot自动配置大量使用了条件化配置。以下是模拟的自动配置示例:

@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@ConditionalOnClass(JdbcTemplate.class)
@ConditionalOnSingleCandidate(DataSource.class)
public class JdbcTemplateAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "spring.jdbc", name = "named-parameters.enabled", havingValue = "true")
    @ConditionalOnMissingBean
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
}

5.4 条件化配置的性能优化

  1. 条件排序:将最可能排除的条件放在前面
  2. 共享条件:对于多个Bean共享的条件,可以在类级别声明
  3. 避免重复评估:确保相同的条件不会被重复评估
  4. 延迟评估:对于耗时的条件,考虑实现ConfigurationCondition接口
public class PerformanceSensitiveCondition implements ConfigurationCondition {
    
    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 只在注册Bean阶段评估
        return performExpensiveCheck();
    }
    
    private boolean performExpensiveCheck() {
        // 耗时的检查逻辑
    }
}

六、高级主题与最佳实践

6.1 条件化配置的测试策略

测试条件化配置需要特殊考虑:

  1. 测试自定义Condition
public class WorkdayConditionTest {
    
    @Test
    public void testWeekday() {
        WorkdayCondition condition = new WorkdayCondition();
        ConditionContext context = mock(ConditionContext.class);
        AnnotatedTypeMetadata metadata = mock(AnnotatedTypeMetadata.class);
        
        // 模拟周一的测试
        try (MockedStatic<LocalDate> mockedLocalDate = mockStatic(LocalDate.class)) {
            mockedLocalDate.when(LocalDate::now).thenReturn(LocalDate.of(2023, 6, 5)); // 周一
            
            assertTrue(condition.matches(context, metadata));
        }
    }
}
  1. 测试条件化配置类
@SpringBootTest
@ActiveProfiles("test")
public class FeatureConfigTest {
    
    @Autowired(required = false)
    private AdvancedFeature advancedFeature;
    
    @Test
    @WithPropertySource(properties = "features.advanced.enabled=true")
    public void whenAdvancedEnabled_thenAdvancedFeatureCreated() {
        assertNotNull(advancedFeature);
    }
    
    @Test
    @WithPropertySource(properties = "features.advanced.enabled=false")
    public void whenAdvancedDisabled_thenAdvancedFeatureNotCreated() {
        assertNull(advancedFeature);
    }
}

6.2 条件化配置的调试技巧

  1. 启用条件评估日志
# application.properties
logging.level.org.springframework.boot.autoconfigure=DEBUG
  1. 使用ConditionEvaluationReport
@SpringBootApplication
public class MyApp {
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(MyApp.class);
        app.setLogStartupInfo(false);
        ConfigurableApplicationContext context = app.run(args);
        
        ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
        report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
            System.out.println(source);
            outcomes.forEach(outcome -> System.out.println("\t" + outcome));
        });
    }
}

6.3 条件化配置的常见陷阱与解决方案

陷阱

原因

解决方案

条件不生效

评估顺序问题

使用@AutoConfigureBefore/@AutoConfigureAfter

循环依赖

条件相互依赖

重构设计,打破循环

性能问题

条件评估耗时

使用ConfigurationCondition延迟评估

测试困难

环境依赖性强

使用测试专用的PropertySource

条件过于复杂

逻辑耦合度高

拆分为多个简单条件

6.4 条件化配置的最佳实践

  1. 命名规范
  2. 条件实现类以Condition结尾
  3. 条件注解以ConditionalOn开头
  4. 设计原则
  5. 单一职责:每个条件只关注一个方面
  6. 明确否定:@ConditionalOnMissingBean@ConditionalOnBean(absent=true)更清晰
  7. 文档完整:为自定义条件添加详细文档说明
  8. 性能优化
  9. 将最可能排除的条件放在前面
  10. 对于耗时的检查,考虑缓存结果
  11. 避免在条件中执行I/O操作
  12. 组合策略
  13. 类级别使用宽泛条件
  14. 方法级别使用具体条件
  15. 避免条件之间的隐式依赖

七、Spring Boot自动配置机制深度解析

7.1 自动配置的条件化基础

Spring Boot的自动配置本质上是一组带有条件注解的@Configuration类,它们位于
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中。

典型的自动配置类结构:

@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "spring.datasource", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
    
    // 其他Bean定义...
}

7.2 自动配置的加载过程

  1. 触发阶段:Spring Boot应用启动时,AutoConfigurationImportSelector处理自动配置
  2. 过滤阶段:根据META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports和条件注解过滤配置类
  3. 排序阶段:根据@AutoConfigureBefore@AutoConfigureAfter@AutoConfigureOrder进行排序
  4. 注册阶段:符合条件的配置类被注册到Spring容器
启动应用

加载AutoConfiguration.imports

过滤条件不满足的配置

排序剩余配置

注册配置类

处理配置类中的@Bean方法

7.3 自定义自动配置

创建自定义自动配置的步骤:

  1. 创建配置类:
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyServiceProperties properties) {
        return new MyServiceImpl(properties);
    }
}
  1. 创建配置属性类:
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
    
    private String endpoint;
    private int timeout = 5000;
    
    // getters and setters
}
  1. 注册自动配置类:


META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中添加:

com.example.MyServiceAutoConfiguration

7.4 自动配置的条件注解最佳实践

  1. 类级别条件:用于宽泛的条件检查(如类存在、属性启用等)
  2. 方法级别条件:用于具体的Bean创建条件(如Bean缺失等)
  3. 合理使用@ConditionalOnMissingBean:确保用户自定义Bean可以覆盖自动配置
  4. 明确配置顺序:使用@AutoConfigureBefore/@AutoConfigureAfter确保依赖关系

八、条件化配置的扩展与高级应用

8.1 基于注解属性的条件判断

我们可以创建更智能的条件注解,根据注解属性动态判断:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnTimeRangeCondition.class)
public @interface ConditionalOnTimeRange {
    
    String from() default "00:00";
    String to() default "23:59";
}

public class OnTimeRangeCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attrs = metadata.getAnnotationAttributes(
            ConditionalOnTimeRange.class.getName());
        LocalTime now = LocalTime.now();
        LocalTime from = LocalTime.parse((String) attrs.get("from"));
        LocalTime to = LocalTime.parse((String) attrs.get("to"));
        
        return now.isAfter(from) && now.isBefore(to);
    }
}

使用示例:

@Configuration
public class TimeBasedConfig {
    
    @Bean
    @ConditionalOnTimeRange(from = "09:00", to = "17:00")
    public OfficeHoursService officeHoursService() {
        // 只在工作时间(9:00-17:00)生效
        return new OfficeHoursServiceImpl();
    }
}

8.2 组合条件注解

对于常用的条件组合,可以创建组合注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ConditionalOnClass(RedisTemplate.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "enabled", havingValue = "true")
@ConditionalOnCloudPlatform(CloudPlatform.NONE)
public @interface ConditionalOnStandaloneRedis {}

使用组合注解:

@Configuration
@ConditionalOnStandaloneRedis
public class StandaloneRedisConfig {
    // 配置
}

8.3 条件化配置的AOP集成

我们可以将条件化配置与AOP结合,实现更灵活的功能控制:

@Aspect
@Configuration
@ConditionalOnProperty("app.features.aop.enabled")
public class FeatureAspect {
    
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceMethods() {}
    
    @Around("serviceMethods() && @annotation(featureAnnotation)")
    public Object checkFeature(ProceedingJoinPoint joinPoint, Feature featureAnnotation) throws Throwable {
        String featureName = featureAnnotation.value();
        if (featureToggleService.isFeatureEnabled(featureName)) {
            return joinPoint.proceed();
        } else {
            throw new FeatureDisabledException(featureName);
        }
    }
}

8.4 条件化配置的性能监控

我们可以创建条件性能监控器来跟踪条件评估的性能:

public class MonitoringCondition implements Condition {
    
    private static final Logger logger = LoggerFactory.getLogger(MonitoringCondition.class);
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        long start = System.nanoTime();
        try {
            boolean result = doMatch(context, metadata);
            return result;
        } finally {
            long duration = System.nanoTime() - start;
            if (duration > 100_000_000) { // 超过100ms
                logger.warn("Slow condition evaluation: {} took {}ms", 
                    metadata, duration / 1_000_000);
            }
        }
    }
    
    private boolean doMatch(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 实际的条件判断逻辑
    }
}

九、实际案例:构建条件化功能模块

9.1 案例背景:多数据源动态路由

假设我们需要实现一个多数据源系统,根据请求特征动态路由到不同的数据源,且某些数据源只在特定条件下可用。

9.2 实现步骤

  1. 定义数据源条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(DataSourceCondition.class)
public @interface ConditionalOnDataSource {
    
    String value();
    boolean required() default true;
}
  1. 实现数据源条件判断
public class DataSourceCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attrs = metadata.getAnnotationAttributes(
            ConditionalOnDataSource.class.getName());
        String dataSourceName = (String) attrs.get("value");
        boolean required = (boolean) attrs.get("required");
        
        Environment env = context.getEnvironment();
        String prefix = "datasource." + dataSourceName;
        
        boolean urlPresent = env.containsProperty(prefix + ".url");
        if (!urlPresent && required) {
            throw new IllegalStateException(
                "Required datasource '" + dataSourceName + "' not configured");
        }
        
        return urlPresent;
    }
}
  1. 配置主数据源
@Configuration
public class PrimaryDataSourceConfig {
    
    @Bean
    @Primary
    @ConditionalOnDataSource("primary")
    public DataSource primaryDataSource(
            @Value("${datasource.primary.url}") String url,
            @Value("${datasource.primary.username}") String username,
            @Value("${datasource.primary.password}") String password) {
        return DataSourceBuilder.create()
            .url(url)
            .username(username)
            .password(password)
            .build();
    }
}
  1. 配置备用数据源
@Configuration
public class SecondaryDataSourceConfig {
    
    @Bean
    @ConditionalOnDataSource(value = "secondary", required = false)
    public DataSource secondaryDataSource(
            @Value("${datasource.secondary.url:}") String url,
            @Value("${datasource.secondary.username:}") String username,
            @Value("${datasource.secondary.password:}") String password) {
        return DataSourceBuilder.create()
            .url(url)
            .username(username)
            .password(password)
            .build();
    }
}
  1. 实现动态路由
public class RoutingDataSource extends AbstractRoutingDataSource {
    
    @Override
    protected Object determineCurrentLookupKey() {
        // 根据当前请求上下文决定数据源
        return DataSourceContextHolder.getDataSource();
    }
    
    @Bean
    @DependsOn({"primaryDataSource", "secondaryDataSource"})
    public DataSource dataSource(
            @Autowired(required = false) DataSource primaryDataSource,
            @Autowired(required = false) DataSource secondaryDataSource) {
        
        RoutingDataSource routingDataSource = new RoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        
        if (primaryDataSource != null) {
            targetDataSources.put("primary", primaryDataSource);
        }
        if (secondaryDataSource != null) {
            targetDataSources.put("secondary", secondaryDataSource);
        }
        
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(primaryDataSource);
        return routingDataSource;
    }
}

9.3 案例解析

这个案例展示了如何将条件化配置应用于复杂场景:

  1. 灵活的数据源配置:只有配置了相应属性的数据源才会被创建
  2. 优先级处理:主数据源是必需的,备用数据源是可选的
  3. 动态路由:运行时根据条件选择合适的数据源
  4. 自动装配:自动处理数据源之间的依赖关系

十、条件化配置的未来发展

10.1 Spring Boot对条件化配置的持续增强

Spring Boot每个版本都在改进条件化配置:

  1. 更丰富的条件注解:如最近的@ConditionalOnWarDeployment
  2. 性能优化:条件评估的缓存和短路优化
  3. 更灵活的扩展点:如ConditionEvaluationReport的增强

10.2 响应式编程中的条件化配置

随着响应式编程的普及,条件化配置也适应这一趋势:

@Configuration
@ConditionalOnReactiveWebApplication
public class ReactiveWebConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public ReactiveRedisTemplate<String, String> reactiveRedisTemplate(
            ReactiveRedisConnectionFactory factory) {
        return new ReactiveRedisTemplate<>(factory, RedisSerializationContext.string());
    }
}

10.3 云原生环境下的条件化配置

Spring Cloud提供了更多云环境相关的条件注解:

@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesConfig {
    
    @Bean
    public KubernetesClient kubernetesClient() {
        return new DefaultKubernetesClient();
    }
}

10.4 条件化配置的最佳实践演进

  1. 声明式条件:倾向于使用注解而非编程式条件
  2. 组合条件:通过组合注解简化复杂条件
  3. 测试友好:设计易于测试的条件实现
  4. 性能优先:在条件评估中考虑性能影响

结语

Spring Boot的条件化配置是一个强大而灵活的特性,它使应用程序能够智能地适应不同的运行环境和配置需求。通过深入理解@Conditional及其衍生注解的工作原理和实现机制,开发者可以构建出更加健壮、灵活且易于维护的Spring Boot应用程序。

从基础的@Conditional注解到各种特定场景的条件注解,再到自定义条件实现和复杂场景的应用,条件化配置为现代Java应用开发提供了强大的基础设施。掌握这一技术,将使你的Spring Boot应用更加智能和自适应。

记住,良好的条件化配置应该:

  • 保持简单和专注
  • 明确表达意图
  • 考虑性能和启动时间
  • 提供清晰的文档
  • 易于测试和维护

希望这篇全面的指南能够帮助你在实际项目中更好地应用Spring Boot条件化配置,构建出更加优秀的应用程序。


关注我?别别别,我怕你笑出腹肌找我赔钱。


头条对markdown的文章显示不太友好,想了解更多的可以关注微信公众号:“Eric的技术杂货库”,有更多的干货以及资料下载。

相关推荐

澳大利亚Find My iPhone被黑 多人被黑客锁机

FindMyiPhone本来是一个用于协助找回被盗手机的好工具,但是现在,澳洲的苹果用户发现他们的FindMyiPhone变成了黑客的帮凶。昨天,这名自称为OlegPliss的黑客使用Fin...

服务器密码错误被锁定怎么解决(服务器密码失效)

#服务器密码错误被锁定解决方案当服务器因多次密码错误导致账户被锁定时,可以按照以下步骤进行排查和解决:##一、确认锁定状态###1.检查账户锁定状态(Linux)```bash#查看账户锁定...

凌晨突发的数据库重大故障,我排查了一整天……

春节期间过得太热闹了,上班确实没啥状态,这不刚发生的一个重大性能故障,排查了整整一天,后面的领导都站成了一排,本次把故障发生的详细分析过程分享给大家!本次故障发生在凌晨,核心应用卡顿非常严重,Orac...

Oracle锁表紧急处理!3招快速解锁方案

开篇:突发故障现场凌晨1点,某电商系统突然卡顿,数千笔支付订单无法完成——数据库出现死锁,技术团队紧急响应...(遇到类似情况的,欢迎在评论区分享经历)一、问题重现:死锁是如何产生的?典型场景:问题根...

JetBrains DataGrip Mac中文破解版V2025.1下载安装教程

DataGripforMac是由JetBrains开发的数据库集成开发环境(IDE),专为数据库管理员和开发人员设计。它支持多种数据库(如MySQL、PostgreSQL、Oracle、SQ...

电脑装安卓系统,安卓X86版5.1 RC1下载

日前,谷歌放出了Android-x865.1的第一个候选版本Android-x865.1RC1,该版本基于Android5.1.1r24Lollipop开发,更新包括大量x86(32位)代...

来来来!一文告诉你Eclipse的正确安装使用姿势,你都清楚吗?

前言本学习笔记是有关如何设置Eclipse的详细说明。即使你天天在使用它,但是,相信我,或许你并不足够了解它。安装Java运行时环境Eclipse是Java应用程序,因此设置Eclipse的第一步是安...

分享收藏的 oracle 11.2.0.4各平台的下载地址

概述oracle11.2.0.4是目前生产环境用的比较多的版本,同时也是很稳定的一个版本。目前官网上已经找不到下载链接了,有粉丝在头条里要求分享一下下载地址。一、各平台下载地址1.1Linuxx...

全面支持Win10:免费虚拟机VMware Player 12下载

IT之家讯免费虚拟机软件VMwareWorkstationPlayer更新了12.0.0版,从版本号可知跨越性较大,上个版本为7.1.2。新版着重打造Win10的支持功能,并且加入了很多其他操作...

深圳尚学堂:Java培训中的小问题(三)

1.删除一张表中所有数据的方式?1.truncatetable命令将快速删除数据表中的所有记录2.delete产生rollback,如果删除在数据量的表速度会很慢,同时会占用很多的rollback...

软件测试必学的16个高频数据库操作及命令,转发收藏

数据库作为软件系统数据的主要存取与操作中心,广泛应用于企业当中。在企业中常用的数据库管理系统有ORACLE、MSSQLSERVER、MySQL等。其中以免费的MySQL最多,特别在中小型互联...

如何计算100万个数据的平均值?MySQL的AVG函数了解下

计算十几个数字的平均数,我们可以用算数公式求出结果;计算成百上千个数字的平均数,我们还可以利用Excel函数公式得出结果。但是,现在已经是大数据时代,数据量动不动就上百万、千万。而Excel行列数有限...

在实际操作过程中如何避免出现SQL注入漏洞

一前言本文将针对开发过程中依旧经常出现的SQL编码缺陷,讲解其背后原理及形成原因。并以几个常见漏洞存在形式,提醒技术同学注意相关问题。最后会根据原理,提供解决或缓解方案。二SQL注入漏洞的原理、形...

Hive性能优化(全面)(hive优化查询速度)

简介:Hadoop的计算框架特性下的HIve有效的优化手段作者:浪尖原文链接本文转载自公众号:Spark学习技巧1.介绍首先,我们来看看Hadoop的计算框架特性,在此特性下会衍生哪些问题?数据量大...

MySQL合集-mysql5.7及mysql8的一些特性

1、Json支持及虚拟列1.1jsonJson在5.7.8原生支持,在8.0引入了json字段的部分更新(jsonpartialupdate)以及两个聚合函数,JSON_OBJECTAGG,JS...

取消回复欢迎 发表评论: