自定义@Conditional使用@Value遇Bean未初始化问题求解
问题分析
你当前的写法行不通,因为Condition的matches方法在Spring解析Bean定义阶段就会执行,这时候dbConfig还没实例化,@Value根本注不进去,configType会是null,判断逻辑完全失效。下面给出两种可行的解决方案,均能避免Bean定义中的if-else,符合你用@Conditional的需求。
方案一:在Condition中直接读取数据库配置
绕过Spring的Bean管理,直接在Condition里用JDBC连接数据库读取配置,不依赖任何未初始化的Bean。
代码示例
修改OnCondition1:
public class OnCondition1 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 从Spring环境中读取数据库连接配置 Environment env = context.getEnvironment(); String url = env.getProperty("spring.datasource.url"); String username = env.getProperty("spring.datasource.username"); String password = env.getProperty("spring.datasource.password"); String driverClassName = env.getProperty("spring.datasource.driver-class-name"); // 直接连接数据库读取配置 try { Class.forName(driverClassName); try (Connection conn = DriverManager.getConnection(url, username, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT config_type FROM config_table WHERE id = 1")) { if (rs.next()) { String configType = rs.getString("config_type"); return "1".equals(configType); } } } catch (Exception e) { // 读取失败时默认不加载该配置类 return false; } return false; } }
OnCondition2同理,仅修改判断逻辑:
public class OnCondition2 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); String url = env.getProperty("spring.datasource.url"); String username = env.getProperty("spring.datasource.username"); String password = env.getProperty("spring.datasource.password"); String driverClassName = env.getProperty("spring.datasource.driver-class-name"); try { Class.forName(driverClassName); try (Connection conn = DriverManager.getConnection(url, username, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT config_type FROM config_table WHERE id = 1")) { if (rs.next()) { String configType = rs.getString("config_type"); return "2".equals(configType); } } } catch (Exception e) { return false; } return false; } }
方案二:提前将数据库配置加载到Spring Environment
用EnvironmentPostProcessor把数据库中的配置提前加载到Spring的Environment中,之后在Condition里直接读取环境属性,更符合Spring生态。
步骤1:实现EnvironmentPostProcessor
public class DbConfigEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 读取数据库连接配置 String url = environment.getProperty("spring.datasource.url"); String username = environment.getProperty("spring.datasource.username"); String password = environment.getProperty("spring.datasource.password"); String driverClassName = environment.getProperty("spring.datasource.driver-class-name"); try { Class.forName(driverClassName); try (Connection conn = DriverManager.getConnection(url, username, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT config_type FROM config_table WHERE id = 1")) { if (rs.next()) { String configType = rs.getString("config_type"); // 将配置存入Spring Environment Map<String, Object> dbProps = new HashMap<>(); dbProps.put("db.config.type", configType); environment.getPropertySources().addFirst(new MapPropertySource("dbConfigProperties", dbProps)); } } } catch (Exception e) { throw new RuntimeException("加载数据库配置失败", e); } } }
步骤2:注册EnvironmentPostProcessor
在src/main/resources/META-INF/spring.factories中添加:
org.springframework.boot.env.EnvironmentPostProcessor=com.yourpackage.DbConfigEnvironmentPostProcessor
步骤3:简化Condition类
public class OnCondition1 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String configType = context.getEnvironment().getProperty("db.config.type"); return "1".equals(configType); } } public class OnCondition2 implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String configType = context.getEnvironment().getProperty("db.config.type"); return "2".equals(configType); } }
注意事项
- 两种方案都要确保数据库连接配置(如
spring.datasource.*)能在Condition执行阶段被读取到(即配置文件已加载)。 - 若数据库不可用,可根据业务需求添加降级逻辑(比如默认加载某类配置或不加载)。
内容的提问来源于stack exchange,提问作者adolzi
相关产品推荐
相关产品推荐

