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

huangapple go评论70阅读模式
英文:

How to programmatically mark a spring bean as @Primary on startup?

问题

我在我的应用程序中有多个 DataSource

标准的 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration 被注解为 @ConditionalOnSingleCandidate(DataSource.class)

我试图以编程方式选择一个 @PrimaryDataSource

我尝试过使用 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.

huangapple
  • 本文由 发表于 2020年8月29日 07:46:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63642104.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定