如何在Spring的Condition/ConfigurationCondition中注入Bean依赖?
Spring的Condition/ConfigurationCondition是在Bean定义解析、注册的早期阶段执行的,此时绝大多数业务Bean还未实例化,所以直接通过@Autowired或BeanFactory获取Bean必然失败。以下是几种可行的替代方案:
方案一:直接在Condition中查询数据库(不依赖Bean)
既然核心需求是通过数据库判断,可跳过中间Bean,直接在Condition里读取数据库配置、建立连接执行查询。这种方式完全在条件评估阶段完成,不依赖任何Bean实例。
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); // 从配置中读取数据库参数 String dbUrl = env.getProperty("spring.datasource.url"); String dbUser = env.getProperty("spring.datasource.username"); String dbPwd = env.getProperty("spring.datasource.password"); String driverClass = env.getProperty("spring.datasource.driver-class-name", "com.mysql.cj.jdbc.Driver"); try { Class.forName(driverClass); // 执行查询判断是否启用配置 try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPwd); PreparedStatement stmt = conn.prepareStatement("SELECT enable FROM system_config WHERE config_key = 'my_config_enable'"); ResultSet rs = stmt.executeQuery()) { return rs.next() && rs.getBoolean("enable"); } } catch (ClassNotFoundException | SQLException e) { // 异常场景默认禁用配置 return false; } } }
方案二:用BeanDefinitionRegistryPostProcessor动态注册配置类
如果必须复用MyBean的逻辑,可放弃@Conditional,改用BeanDefinitionRegistryPostProcessor。这个处理器在所有Bean定义注册完成后、实例化前执行,可手动初始化MyBean并根据结果动态注册MyConfig。
@Component public class MyConfigRegistrar implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) registry; // 手动实例化MyBean(需确保其依赖的Bean定义已注册) MyBean myBean = beanFactory.createBean(MyBean.class); boolean shouldEnable = myBean.checkIfMyConfigShouldEnable(); if (shouldEnable) { // 注册MyConfig的Bean定义 BeanDefinition configDef = BeanDefinitionBuilder.genericBeanDefinition(MyConfig.class).getBeanDefinition(); registry.registerBeanDefinition("myConfig", configDef); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 无需额外操作 } }
注意:若MyBean有复杂依赖,需确保这些依赖的Bean定义已完成注册,否则createBean会抛出异常。
方案三:结合@ConditionalOnBean与SpEL表达式
如果MyBean无循环依赖且可提前初始化,可使用@ConditionalOnBean确保MyBean存在,再通过@ConditionalOnExpression调用其方法判断:
@Configuration @ConditionalOnBean(MyBean.class) @ConditionalOnExpression("#{myBean.checkIfMyConfigShouldEnable()}") public class MyConfig { // 配置类内容 }
风险提示:若MyBean依赖MyConfig中的Bean,会触发循环依赖;需确保MyBean的初始化时机早于条件评估。
方案四:监听上下文事件动态调整Bean
允许在上下文初始化完成后再决定的话,可监听ContextRefreshedEvent(此时所有Bean已实例化),通过MyBean判断后动态注册/销毁MyConfig:
@Component public class MyConfigActivator implements ApplicationListener<ContextRefreshedEvent> { @Autowired private MyBean myBean; @Autowired private ConfigurableApplicationContext context; @Override public void onApplicationEvent(ContextRefreshedEvent event) { boolean shouldEnable = myBean.checkIfMyConfigShouldEnable(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); if (shouldEnable && !beanFactory.containsBean("myConfig")) { MyConfig myConfig = beanFactory.createBean(MyConfig.class); beanFactory.registerSingleton("myConfig", myConfig); } else if (!shouldEnable && beanFactory.containsBean("myConfig")) { ((DefaultSingletonBeanRegistry) beanFactory).destroySingleton("myConfig"); } } }
缺点:MyConfig的Bean会延迟到上下文初始化完成后才注册,可能影响依赖它的Bean的启动时机。
内容的提问来源于stack exchange,提问作者Arunabh

