如何在启动时以编程方式将Spring bean标记为@Primary?

问题描述

我的应用程序中有多个DataSource

标准org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration带有@ConditionalOnSingleCandidate(DataSource.class)

我正在尝试以编程方式选择@Primary DataSource

我尝试过一个beanfactoryPostProcessor,它天真地选择了第一个数据源并将其标记为主要):

    @Bean
    public beanfactoryPostProcessor beanfactoryPostProcessor() {
        return this::setPrimaryDataSource;
    }

    public void setPrimaryDataSource(ConfigurableListablebeanfactory beanfactory) {

        // Get all DataSource bean names
        String[] dataSourceBeanNames = beanfactory.getBeanNamesForType(DataSource.class);

        // Find primaryBeanName
        String primaryBeanName = dataSourceBeanNames.length > 0 ? dataSourceBeanNames[0] : null;

        // Return appropriate bean
        assert primaryBeanName != null;
        BeanDeFinition beanDeFinition = beanfactory.getBeanDeFinition(primaryBeanName);
        beanDeFinition.setPrimary(true);
        LOGGER.info("Primary DataSource: {}",primaryBeanName);

    }

但是,这似乎不起作用-@ConditionalOnSingleCandidate(DataSource.class)上的HibernateJpaConfiguration检查仍然失败。

还有其他地方可以放置这段代码,以便在检查@ConditionalOnSingleCandidate之前执行该代码吗?

解决方法

如果您的代码在具有@Configuration的类中,则该方法必须是静态的,以便在创建任何bean之前更新bean定义。

这是PropertySourcesPlaceholderConfigurer的示例。

@Configuration
public class AppConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
         return new PropertySourcesPlaceholderConfigurer();
    }

}

documentation中:

您可以将@Bean方法声明为静态方法,从而允许在不将其包含配置类创建为实例的情况下调用它们。在定义后处理器Bean(例如BeanFactoryPostProcessor或BeanPostProcessor类型)时,这特别有意义,因为此类Bean在容器生命周期的早期进行了初始化,并且应避免在那时触发配置的其他部分。

,

BeanFactoryPostProcessor 为我工作:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        // logic to retrieve your bean name
        String beanName = beanFactory.getBeanNamesForType(MyService.class)[0];
        
        BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
        bd.setPrimary(true);
    }

}