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



我有一个批量配置。我看到批处理过程是默认使用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数据源?我怎么能强迫春季批使用默认MySQL而不是InMemoryMap?

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

当您有两个(或更多)相互依赖的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的自动配置为您自动创建一个HikariDataSource,这是我通常采用的方法。Spring Bean的名称将是dataSource,它将通过setDataSource()自动连接到DefaultBatchConfigurer

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

如果你仍然想手动配置你的DataSource,那么我建议你不是扩展DefaultBatchConfigurer,而是在配置类中为它定义一个自定义bean,在那里你可以直接传递你的自定义DataSource(基于我目前所知道的你的用例)。

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

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

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

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

更新@SpringBootApplication类:

@SpringBootApplication(
exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
}
)
public class App {

public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}