英文:
How to programmatically mark a spring bean as @Primary on startup?
问题
我在我的应用程序中有多个 DataSource。
标准的 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration 被注解为 @ConditionalOnSingleCandidate(DataSource.class)。
我试图以编程方式选择一个 @Primary 的 DataSource。
我尝试过使用 BeanFactoryPostProcessor 来天真地选择第一个 DataSource 并标记为主要:
@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
    return this::setPrimaryDataSource;
}
public void setPrimaryDataSource(ConfigurableListableBeanFactory beanFactory) {
    // 获取所有 DataSource 的 bean 名称
    String[] dataSourceBeanNames = beanFactory.getBeanNamesForType(DataSource.class);
    // 查找 primaryBeanName
    String primaryBeanName = dataSourceBeanNames.length > 0 ? dataSourceBeanNames[0] : null;
    // 返回适当的 bean
    assert primaryBeanName != null;
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition(primaryBeanName);
    beanDefinition.setPrimary(true);
    LOGGER.info("Primary DataSource: {}", primaryBeanName);
}
然而,这似乎不起作用 - HibernateJpaConfiguration 上的 @ConditionalOnSingleCandidate(DataSource.class) 检查仍然失败。
是否有其他地方可以将此代码放置,以便在检查 @ConditionalOnSingleCandidate 之前执行它?
英文:
I have multiple DataSources in my application.
The standard org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration is annotated with @ConditionalOnSingleCandidate(DataSource.class)
I am attempting to select a @Primary DataSource programmatically.
I have tried a BeanFactoryPostProcessor that naively selects the first DataSource and marks as primary):
    @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);
    }
However, this does not appear to work - the @ConditionalOnSingleCandidate(DataSource.class) check on HibernateJpaConfiguration still fails.
Is there anywhere else I can put this code such that it will be executed before the check for @ConditionalOnSingleCandidate?
答案1
得分: 2
`BeanFactoryPostProcessor` 对我起了作用:
    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    
            // 检索您的 bean 名称的逻辑
            String beanName = beanFactory.getBeanNamesForType(MyService.class)[0];
            
            BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
            bd.setPrimary(true);
        }
    }
英文:
BeanFactoryPostProcessor worked for me:
@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);
	}
}
答案2
得分: 1
如果您的代码位于带有@Configuration的类中,则该方法需要是静态的,以便在任何bean创建之前更新bean定义。
以下是PropertySourcesPlaceholderConfigurer的示例。
@Configuration
public class AppConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
         return new PropertySourcesPlaceholderConfigurer();
    }
}
在文档中:
> 您可以将@Bean方法声明为静态方法,允许在不创建包含其配置类实例的情况下调用它们。这在定义后置处理器bean时特别有意义(例如,BeanFactoryPostProcessor或BeanPostProcessor类型的bean),因为这些bean在容器生命周期的早期进行初始化,应避免在该点触发配置的其他部分。
英文:
If your code is in a class with @Configuration, the method need to be static in order to update the bean definition before any bean creation.
Here is a sample for PropertySourcesPlaceholderConfigurer.
@Configuration
public class AppConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
         return new PropertySourcesPlaceholderConfigurer();
    }
}
In the documentation:
> You may declare @Bean methods as static, allowing for them to be called without creating their containing configuration class as an instance. This makes particular sense when defining post-processor beans (for example, of type BeanFactoryPostProcessor or BeanPostProcessor), since such beans get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论