Spring Batch @Primary 注释覆盖不正确的bean

Posted

技术标签:

【中文标题】Spring Batch @Primary 注释覆盖不正确的bean【英文标题】:Spring Batch @Primary annotation overriding incorrect bean 【发布时间】:2017-07-30 07:43:30 【问题描述】:

我跟着this tutorial 用Java 配置了一个Spring Batch 作业。它通过使用由每个数据源实现的接口来提供多个数据源。

这是我目前所拥有的:

InfrastructureConfig.java

public interface InfrastructureConfiguration 
    @Bean
    DataSource dataSource();

MySQLConfig.java

@Configuration
@Primary
public class mysqlConfiguration implements InfrastructureConfiguration 

    @Bean
    public DataSource dataSource() 
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/employees?useSSL=false");
        dataSource.setUsername("testing");
        dataSource.setPassword("testing");
        return dataSource;
    

PostgreSQLConfig.java

@Configuration
public class PostgreSQLConfiguration implements InfrastructureConfiguration 

    @Bean
    public DataSource dataSource() 
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
        dataSource.setUsername("postgres");
        dataSource.setPassword("testing");
        return dataSource;
    

JobConfig.java

@Configuration
public class JobConfig 

    @Autowired
    private InfrastructureConfig infrastructureConfig

    ....

通过对我的 MySQLConfig 使用 @Primary 注释,我希望使用 mySQLConfig bean。相反,我得到了这个:

2017-03-09 12:46:21.422 INFO 1496 --- [main] o.s.b.f.s.DefaultListableBeanFactory:用不同的定义覆盖 bean 'dataSource' 的 bean 定义:替换 [Root bean: class [null];范围=;摘要=假;懒惰初始化=假;自动线模式=3;依赖检查=0;自动接线候选=真;主要=假; factoryBeanName=mySQLConfiguration;工厂方法名=数据源;初始化方法名=空; destroyMethodName=(推断);在类路径资源 [config/MySQLConfiguration.class]] 中定义为 [Root bean: class [null];范围=;摘要=假;懒惰初始化=假;自动线模式=3;依赖检查=0;自动接线候选=真;主要=假; factoryBeanName=postgreSQLConfiguration;工厂方法名=数据源;初始化方法名=空; destroyMethodName=(推断);在类路径资源 [config/PostgreSQLConfiguration.class] 中定义

它使用 postgreSQLConfig bean 覆盖 mySQLConfig bean,因此使用 postgresql 驱动程序。问题是,为什么?

【问题讨论】:

【参考方案1】:

@Primary 放在方法上(@Bean 旁边)而不是类级别。

【讨论】:

如果我这样做,我会收到一条错误消息,指出“Field InfrastructureConfig 需要一个 bean,但找到了 2 个:mySQLConfiguration 和 postgreSQLConfiguration”【参考方案2】:

尝试使用@Qualifier(name = "") 然后明确选择您想要的实例。

您在接口方法上有@Bean 注释也很奇怪。 此外,使用自动装配 @Configuration 类对我来说也是一种不好的做法。它们是用于实例化 bean 的基于 java 的配置。

【讨论】:

我试过@Qualifier("mySQLConfig")@Resource(name="mySQLConfig") 都没有运气。至于其余的,我只是按照上面链接的 Spring Batch 教程中指定的内容进行操作。那么,该教程肯定存在根本性错误。但是去掉接口方法上的@Bean注解并没有什么不同,用@Inject注解代替@Autowire也是徒劳的。 嗯。从所有方法中删除 @Bean 注释。实际上将子配置上的@Configuration 更改为@Component。我猜你已经启用了组件扫描,对吧?我还看到名称不匹配 - InfrastructureConfig 和 InfrastructureConfiguration 。会不会是问题? 名称不匹配只是我在评论中的错误,对不起。我已经尝试了您的建议,但仍然没有运气。这是一个更复杂系统的一小部分,我认为问题可能出在其他地方。找到答案后,我会更新答案(希望很快) 这行为正确,请参阅错误jira.spring.io/browse/SPR-13980 "@Primary 对这种按名称覆盖的行为没有影响,因为它仅适用于注入点解析:即当多个在 bean 定义注册表中找到了匹配的 bean,但是对于特定的注入点只选择一个。这在概念上与重写 bean 定义非常不同,后者导致在注册表中硬替换,以前注册的 bean 不再可用。 " 关于 Spring bean 的唯一 ID 的有趣阅读。 intertech.com/Blog/clarifying-spring-framework-ids-and-names 这不是使用@Primary 注解的答案,而是阐明了在创建具有相同标识的多个bean 时Spring 的行为方式。【参考方案3】:

你可以试试@Profiles注解。

@Profile("mySql")注释mySql配置类,用@Profile("myPostgresql")注释Postgres配置

然后用@ActiveProfiles("mySql") 注释JobConfig 配置。这样JobConfig 应该会忽略 postgres 配置。

假设你在测试中使用 MySQL,你可以用 @ActiveProfiles("mySql") 注释测试类

Spring Profiles documentation

文档中的示例:

@Configuration
public class AppConfig 

  @Bean("dataSource")
  @Profile("development") 
  public DataSource standaloneDataSource() 
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("classpath:com/bank/config/sql/schema.sql")
        .addScript("classpath:com/bank/config/sql/test-data.sql")
        .build();
  

  @Bean("dataSource")
  @Profile("production") 
  public DataSource jndiDataSource() throws Exception 
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
  

【讨论】:

以上是关于Spring Batch @Primary 注释覆盖不正确的bean的主要内容,如果未能解决你的问题,请参考以下文章

@Primary 等效于自动装配的 Spring JPA 存储库

Spring IOC容器注解 @Autowired@Resource@Primary@Value的用法

Spring Batch 无法通过 DB (postgres) 获取作业锁

如何评估在应用程序中是使用spring batch还是scheduler?

spring batch(二):核心部分:配置Spring batch

带有单个数据源的 SpringBoot + Batch + Cloud Task @EnableTask 注释导致“序列不存在”问题