【问题标题】:Error creating bean with name 'batchDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?创建名为“batchDataSource”的 bean 时出错:请求的 bean 当前正在创建中:是否存在无法解析的循环引用?
【发布时间】:2021-11-21 13:29:17
【问题描述】:

我有一个批处理配置。我看到批处理默认使用InMemoryMap。相反,我需要使用 MySQL 批量发送所有执行细节。但是当我使用以下代码时,出现以下错误,

创建名为“batchDataSource”的 bean 时出错:请求的 bean 是 当前正在创建中:是否存在无法解析的循环引用?

@Configuration
@EnableBatchProcessing
public class BatchProcess extends DefaultBatchConfigurer {

    private @Autowired Environment env;

    @Bean
    @StepScope
    public ItemReader reader() {
        ...
    }

    @Bean
    @StepScope
    public ItemProcessor processor() {
        ...
    }

    @Bean
    @StepScope
    public ItemWriter writer() {
        ...
    }

    @Bean
    @Primary
    public DataSource batchDataSource() {
        HikariDataSource hikari = new HikariDataSource();
        hikari.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        hikari.setJdbcUrl(env.getProperty("spring.datasource.url"));
        hikari.setUsername(env.getProperty("spring.datasource.username"));
        hikari.setPassword(env.getProperty("spring.datasource.password"));
        return hikari;
    }

    public JobRepository getJobRepository() {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(batchDataSource());
        factory.setTransactionManager(manager());
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    public PlatformTransactionManager manager() {
        return new ResourcelessTransactionManager();
    }

    @Bean
    public Step step() {
        return stepBuilderFactory.get("step")
                .chunk(1000)
                .reader(reader())
                .processor(processor())
                .writer(writer())
                .build();
    }

    @Bean
    public Job job() {
        return jobBuilderFactory.get("job")
                .flow(step())
                .end()
                .build();
    }

    @Bean
    public JobLauncher getJobLauncher() {
        SimpleJobLauncher launcher = new SimpleJobLauncher();
        launcher.setJobRepository(createJobRepository());
        return launcher;
    }
}

在我正在使用的属性文件中,

spring.batch.job.enabled=false
spring.batch.initialize-schema=always

那么我错过了什么?我正在使用 JPA。甚至为什么它不使用可用的 JPA 数据源?如何强制 Spring 批处理使用默认 MySQL 而不是 InMemoryMap?

【问题讨论】:

  • stepBuilderFactoryjobBuilderFactory 如何在 BatchProcess 类中自动连接?这是关键,但未在您的描述中显示。

标签: java spring-boot spring-batch datasource


【解决方案1】:

首先,在 pom.xml 中显式定义 mysql-connector 依赖,并从项目中删除与内存映射相关的任何内容。

  <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
   </dependency>

如果您想使用 bean 手动定义自己的配置,那么您不能使用 AutoConfiguration 类,因为它们会在启动时自动为您创建所需的 bean,如果您定义自己的自定义 DB 配置类,这可能会导致问题.因此,您必须排除DataSourceAutoConfigurationHibernateJpaAutoConfigurationDataSourceTransactionManagerAutoConfiguration 才能解决此问题。

只需更新@SpringBootApplication 类:

@SpringBootApplication(
      exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class
      }
    )
    public class App {
    
      public static void main(String[] args) {
        SpringApplication.run(App.class, args);
      }
    }

【讨论】:

    【解决方案2】:

    您收到的错误消息可能不是最清楚的,但它应该为您指明正确的方向。您的代码中似乎有一个循环依赖

    当您有两个(或多个)bean 相互依赖时会发生这种情况,从而阻止创建一个而没有另一个存在(反之亦然) - 众所周知的鸡和蛋问题。您通常可以通过 setter 注入和某种构造后初始化来避免这种情况。

    我认为您已经通过扩展DefaultBatchConfigurer 然后定义@Bean 注释方法getJobLauncher() 来创建这种情况,该方法直接调用DefaultBatchConfigurercreateJobRepository() 方法而不确保首先在其中设置DataSource DefaultBatchConfigurer.

    这完全没有必要,因为DefaultBatchConfigurer 已经以正确的顺序为您创建了JobRepositoryJobExplorerJobLauncher

    来自DefaultBatchConfigurer

    @PostConstruct
        public void initialize() {
            try {
                this.jobRepository = createJobRepository();
                this.jobExplorer = createJobExplorer();
                this.jobLauncher = createJobLauncher();
            } catch (Exception e) {
                throw new BatchConfigurationException(e);
            }
        }
    

    如果你要扩展DefaultBatchConfigurer,那么我建议你从你的代码中去掉以下方法

    • getJobRepository()
    • manager()
    • getJobLauncher()

    从您的代码示例中,您似乎已经在设置以下属性(在您的application.properties 文件中?):

    spring.datasource.jdbcUrl=...
    spring.datasource.username=...
    spring.datasource.password=...
    spring.datasource.driverClassName=...
    

    这应该足以让 Spring 的 AutoConfiguration 自动为您创建一个 Hikari DataSource,这是我通常采用的方法。 Spring Bean 名称将是 dataSource,这将通过 setDataSource() 自动装配到 DefaultBatchConfigurer

    但是,在您的代码示例中,您还定义了一个名为 batchDataSource() 的带注释的 @Bean 方法,它看起来与您应该从 Spring AutoConfiguration 收到的内容没有什么不同。只要你配置了前面提到的spring.datasource属性,你应该也可以消除batchDataSource(),但我认为没有必要,所以你的选择。

    如果您仍想手动配置您的DataSource,那么我建议您不要扩展DefaultBatchConfigurer,而是改为在配置类,您可以在其中直接传递您的自定义DataSource(基于我目前对您的用例的了解)。

    @Bean
    public BatchConfigurer batchConfigurer(){
        return new DefaultBatchConfigurer( batchDataSource() );
    }
    

    【讨论】:

      猜你喜欢
      • 2020-03-24
      • 2017-10-01
      • 1970-01-01
      • 2017-05-29
      • 1970-01-01
      • 2018-09-05
      • 2021-05-15
      • 1970-01-01
      • 2015-06-29
      相关资源
      最近更新 更多