为什么在尝试启动多个 Spring 批处理作业时出现此错误? bean 'jobLauncher'..无法注册
Why am I obtaining this error trying to start multiple Spring Batch Jobs? The bean 'jobLauncher'....could not be registerd
我正在开发一个 Spring 批处理应用程序,其中包含两个不同的 Job bean(代表两个不同的作业)。这两项工作都必须由我的应用程序执行(目前它可以顺序地和并行地完成。目前并不那么重要)。
为了实现此行为,我尝试遵循此文档,但我发现了几个问题:https://newbedev.com/spring-batch-running-multiple-jobs-in-parallel
我会尽量说明我的情况和遇到的问题是什么:
首先我有这个配置 class 我的两个 Jobs 对象(和相关步骤)被声明:
@Configuration
public class UpdateInfoBatchConfig {
private static final String PROPERTY_REST_API_URL = "rest.api.url";
@Autowired
private NotaryListServiceAdapter notaryListServiceAdapter;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private NotaryService notaryService;
@Bean("firstStepItemReader")
public ItemReader<NotaryDistrict> itemReader(Environment environment, RestTemplate restTemplate) throws IllegalStateException, URISyntaxException {
System.out.println("itemReader() START !!!");
return new RESTNotaryDistrictsReader();
}
@Bean("firstStepItemWriter")
public ItemWriter<NotaryDistrict> itemWriter() {
return new LoggingItemWriter();
}
@Bean("secondStepItemReader")
public ItemReader<NotaryDistrict> secondStepReader(Environment environment) throws IllegalStateException {
System.out.println("secondStepItemReader() creation !!!");
return new SecondStepItemReader();
}
@Bean("secondStepItemProcessor")
public ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor() {
return new SecondStepItemProcessor();
}
@Bean("secondStepItemWriter")
public ItemWriter<NotaryDistrict> secondStepItemWriter() {
return new SecondStepItemWriter();
}
/**
************************************ UPDATE NOTARY DISTRICTS LIST JOB SECTION ********************************************
*/
/**
* Creates a bean that represents the first step of the batch.
* How it works:
* 1) Call an external API in order to retrieve notary districts list
* 2) Return notary district one by one to the second step
* @param reader a custom reader calling an external API
* @param writer
* @param stepBuilderFactory
* @return
*/
@Bean("firstStep")
public Step firstStep(@Qualifier("firstStepItemReader") ItemReader<NotaryDistrict> reader,
@Qualifier("firstStepItemWriter") ItemWriter<NotaryDistrict> writer,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("updateNotaryDistrictsStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(reader)
.writer(writer)
.build();
}
@Bean("secondStep")
public Step secondStep(@Qualifier("secondStepItemReader") ItemReader<NotaryDistrict> secondStepItemReader,
@Qualifier("secondStepItemProcessor") ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor,
@Qualifier("secondStepItemWriter") ItemWriter<NotaryDistrict> secondStepItemWriter,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("secondStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(secondStepItemReader)
.processor(secondStepItemProcessor)
.writer(secondStepItemWriter)
.build();
}
@Bean("updateNotaryDistrictsJob")
public Job updateNotaryDistrictsJob(JobBuilderFactory jobBuilderFactory,
@Qualifier("firstStep") Step firstStep,
@Qualifier("secondStep") Step secondStep) {
return jobBuilderFactory.get("updateNotaryDistrictsJob")
.start(firstStep)
.next(secondStep)
//.next(playerSummarization())
.build();
}
@Bean
public ExecutionContext executionContext() {
return new ExecutionContext();
}
/**
************************************ UPDATE NOTARY LIST JOB SECTION ********************************************
*/
@Bean()
public ItemReaderAdapter serviceItemReader() {
ItemReaderAdapter reader = new ItemReaderAdapter();
reader.setTargetObject(notaryListServiceAdapter);
reader.setTargetMethod("nextNotaryElement");
return reader;
}
@Bean
public Step readNotaryListStep(){
return steps.get("readNotaryListStep").
<Integer,Integer>chunk(1)
.reader(serviceItemReader())
.processor(new NotaryDetailsEnrichProcessor(notaryService))
.writer(new ConsoleItemWriter())
.build();
}
@Bean("updateNotaryListInfoJob")
public Job updateNotaryListInfoJob(){
return jobs.get("updateNotaryListInfoJob")
.incrementer(new RunIdIncrementer())
.start(readNotaryListStep())
.build();
}
}
然后,我第一时间创建了另一个 SpringBatchExampleJobLauncher 启动器 class。这工作正常,最初用于启动单个作业(我想我必须更改此启动器的逻辑 class 以执行两个作业而不是一个作业):
public class SpringBatchExampleJobLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBatchExampleJobLauncher.class);
private final Job job;
private final JobLauncher jobLauncher;
private ExecutionContext executionContext;
@Autowired
public SpringBatchExampleJobLauncher(@Qualifier("updateNotaryDistrictsJob") Job job,
JobLauncher jobLauncher,
ExecutionContext executionContext) {
this.job = job;
this.jobLauncher = jobLauncher;
this.executionContext = executionContext;
}
//@Scheduled(cron = "0 */3 * * * *")
@Scheduled(cron = "0/30 * * * * *")
public void runSpringBatchExampleJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
LOGGER.info("Spring Batch example job was started");
List<NotaryDistrict> notaryDistrictsList = new ArrayList<NotaryDistrict>();
executionContext.put("notaryDistrictsList", notaryDistrictsList);
jobLauncher.run(job, newExecution());
LOGGER.info("Spring Batch example job was stopped");
}
private JobParameters newExecution() {
Map<String, JobParameter> parameters = new HashMap<>();
JobParameter parameter = new JobParameter(new Date());
parameters.put("currentTime", parameter);
return new JobParameters(parameters);
}
}
如您所见,这个 class 非常简单:它的构造函数执行一个特定的作业(由定义到先前配置中的 @Qualifier 标识 class), JobLauncher 以便 运行 dis 作业和 ExecutionContext.
然后它包含 运行SpringBatchExampleJob() 即 运行 这个作业每 30 秒一次(由 CRON 指定例外)。
好的....因此,为了开始我的 2 个工作,我认为我需要以与所示类似的方式更改此 SpringBatchExampleJobLauncher这里:https://newbedev.com/spring-batch-running-multiple-jobs-in-parallel
所以我做了什么。首先,我将 ThreadPoolTaskExecutor 和 JobLauncher bean 定义添加到我的 UpdateInfoBatchConfig 配置中 class,变成这样:
@Configuration
public class UpdateInfoBatchConfig {
private static final String PROPERTY_REST_API_URL = "rest.api.url";
@Autowired
private NotaryListServiceAdapter notaryListServiceAdapter;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private NotaryService notaryService;
@Bean("firstStepItemReader")
public ItemReader<NotaryDistrict> itemReader(Environment environment, RestTemplate restTemplate) throws IllegalStateException, URISyntaxException {
System.out.println("itemReader() START !!!");
return new RESTNotaryDistrictsReader();
}
@Bean("firstStepItemWriter")
public ItemWriter<NotaryDistrict> itemWriter() {
return new LoggingItemWriter();
}
@Bean("secondStepItemReader")
public ItemReader<NotaryDistrict> secondStepReader(Environment environment) throws IllegalStateException {
System.out.println("secondStepItemReader() creation !!!");
return new SecondStepItemReader();
}
@Bean("secondStepItemProcessor")
public ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor() {
return new SecondStepItemProcessor();
}
@Bean("secondStepItemWriter")
public ItemWriter<NotaryDistrict> secondStepItemWriter() {
return new SecondStepItemWriter();
}
/**
* Creates a bean that represents the first step of the batch.
* How it works:
* 1) Call an external API in order to retrieve notary districts list
* 2) Return notary district one by one to the second step
* @param reader a custom reader calling an external API
* @param writer
* @param stepBuilderFactory
* @return
*/
@Bean("firstStep")
public Step firstStep(@Qualifier("firstStepItemReader") ItemReader<NotaryDistrict> reader,
@Qualifier("firstStepItemWriter") ItemWriter<NotaryDistrict> writer,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("updateNotaryDistrictsStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(reader)
.writer(writer)
.build();
}
@Bean("secondStep")
public Step secondStep(@Qualifier("secondStepItemReader") ItemReader<NotaryDistrict> secondStepItemReader,
@Qualifier("secondStepItemProcessor") ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor,
@Qualifier("secondStepItemWriter") ItemWriter<NotaryDistrict> secondStepItemWriter,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("secondStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(secondStepItemReader)
.processor(secondStepItemProcessor)
.writer(secondStepItemWriter)
.build();
}
@Bean("updateNotaryDistrictsJob")
public Job updateNotaryDistrictsJob(JobBuilderFactory jobBuilderFactory,
@Qualifier("firstStep") Step firstStep,
@Qualifier("secondStep") Step secondStep) {
return jobBuilderFactory.get("updateNotaryDistrictsJob")
.start(firstStep)
.next(secondStep)
//.next(playerSummarization())
.build();
}
@Bean
public ExecutionContext executionContext() {
return new ExecutionContext();
}
/**
************************************ UPDATE NOTARY LIST JOB ********************************************
*/
@Bean()
public ItemReaderAdapter serviceItemReader() {
ItemReaderAdapter reader = new ItemReaderAdapter();
reader.setTargetObject(notaryListServiceAdapter);
reader.setTargetMethod("nextNotaryElement");
return reader;
}
@Bean
public Step readNotaryListStep(){
return steps.get("readNotaryListStep").
<Integer,Integer>chunk(1)
.reader(serviceItemReader())
.processor(new NotaryDetailsEnrichProcessor(notaryService))
.writer(new ConsoleItemWriter())
.build();
}
@Bean("updateNotaryListInfoJob")
public Job updateNotaryListInfoJob(){
return jobs.get("updateNotaryListInfoJob")
.incrementer(new RunIdIncrementer())
.start(readNotaryListStep())
.build();
}
/**
************************************ MULTIPLE JOB CONFIGURATION ********************************************
*/
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(15);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setQueueCapacity(30);
return taskExecutor;
}
@Bean
public JobLauncher jobLauncher(ThreadPoolTaskExecutor taskExecutor, JobRepository jobRepository){
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(taskExecutor);
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
}
如您所见,最后两个 bean 是 ThreadPoolTaskExecutor 和我的 JobLauncher bean。
然后我更改了 SpringBatchExampleJobLauncher 以便使用此启动器并执行我的两个作业而不是一个作业,这就是我所做的:
/**
* This bean schedules and runs our Spring Batch job.
*/
@Component
public class SpringBatchExampleJobLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBatchExampleJobLauncher.class);
@Autowired
private JobLauncher jobLauncher;
@Autowired
@Qualifier("updateNotaryDistrictsJob")
private Job updateNotaryDistrictsJob;
@Autowired
@Qualifier("updateNotaryListInfoJob")
private Job updateNotaryListInfoJob;
@Scheduled(cron = "0/30 * * * * *")
public void run1(){
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(updateNotaryDistrictsJob, jobParameters);
}catch (Exception ex){
LOGGER.error(ex.getMessage());
}
}
@Scheduled(cron = "0/50 * * * * *")
public void run2(){
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(updateNotaryListInfoJob, jobParameters);
}catch (Exception ex){
LOGGER.error(ex.getMessage());
}
}
}
如您所见,我现在正在注入之前定义的 JobLauncher bean 和我的两个作业 bean(在我的配置 class 中定义)。然后我定义了 运行1() 和应该 运行 的 运行2() 方法当满足 CRON 表达式时,我的两个注入作业。
问题是我现在在我的 stracktrace 中收到以下错误并且没有执行任何操作:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'jobLauncher', defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/notariato/updateInfo/UpdateInfoBatchConfig.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
基本上,在我看来,这个错误是在告诉我,我试图注入我的启动器 class 的 JobLauncher bean 尚未定义到我的 UpdateInfoBatchConfig 配置 class。但这正是我所期望的,因为我将我的 bean 定义到配置 class 中,然后我将它注入到要使用的启动器 class 中。
怎么了?我错过了什么?我该如何尝试解决这个问题?
这是因为您在应用程序上下文中定义了一个 JobLauncher
bean,并且 Spring Batch 也通过 @EnableBatchProcessing
(参见它的 Javadoc)定义了那个 bean。
如果您想使用自定义 JobLauncher
,您应该提供一个 BatchConfigurer
bean 并覆盖 getJobLauncher
。一种方法是使您的配置 类 扩展 DefaultBatchConfigurer
并覆盖 createJobLauncher()
。这在文档 here.
中有更详细的解释
我正在开发一个 Spring 批处理应用程序,其中包含两个不同的 Job bean(代表两个不同的作业)。这两项工作都必须由我的应用程序执行(目前它可以顺序地和并行地完成。目前并不那么重要)。
为了实现此行为,我尝试遵循此文档,但我发现了几个问题:https://newbedev.com/spring-batch-running-multiple-jobs-in-parallel
我会尽量说明我的情况和遇到的问题是什么:
首先我有这个配置 class 我的两个 Jobs 对象(和相关步骤)被声明:
@Configuration
public class UpdateInfoBatchConfig {
private static final String PROPERTY_REST_API_URL = "rest.api.url";
@Autowired
private NotaryListServiceAdapter notaryListServiceAdapter;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private NotaryService notaryService;
@Bean("firstStepItemReader")
public ItemReader<NotaryDistrict> itemReader(Environment environment, RestTemplate restTemplate) throws IllegalStateException, URISyntaxException {
System.out.println("itemReader() START !!!");
return new RESTNotaryDistrictsReader();
}
@Bean("firstStepItemWriter")
public ItemWriter<NotaryDistrict> itemWriter() {
return new LoggingItemWriter();
}
@Bean("secondStepItemReader")
public ItemReader<NotaryDistrict> secondStepReader(Environment environment) throws IllegalStateException {
System.out.println("secondStepItemReader() creation !!!");
return new SecondStepItemReader();
}
@Bean("secondStepItemProcessor")
public ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor() {
return new SecondStepItemProcessor();
}
@Bean("secondStepItemWriter")
public ItemWriter<NotaryDistrict> secondStepItemWriter() {
return new SecondStepItemWriter();
}
/**
************************************ UPDATE NOTARY DISTRICTS LIST JOB SECTION ********************************************
*/
/**
* Creates a bean that represents the first step of the batch.
* How it works:
* 1) Call an external API in order to retrieve notary districts list
* 2) Return notary district one by one to the second step
* @param reader a custom reader calling an external API
* @param writer
* @param stepBuilderFactory
* @return
*/
@Bean("firstStep")
public Step firstStep(@Qualifier("firstStepItemReader") ItemReader<NotaryDistrict> reader,
@Qualifier("firstStepItemWriter") ItemWriter<NotaryDistrict> writer,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("updateNotaryDistrictsStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(reader)
.writer(writer)
.build();
}
@Bean("secondStep")
public Step secondStep(@Qualifier("secondStepItemReader") ItemReader<NotaryDistrict> secondStepItemReader,
@Qualifier("secondStepItemProcessor") ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor,
@Qualifier("secondStepItemWriter") ItemWriter<NotaryDistrict> secondStepItemWriter,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("secondStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(secondStepItemReader)
.processor(secondStepItemProcessor)
.writer(secondStepItemWriter)
.build();
}
@Bean("updateNotaryDistrictsJob")
public Job updateNotaryDistrictsJob(JobBuilderFactory jobBuilderFactory,
@Qualifier("firstStep") Step firstStep,
@Qualifier("secondStep") Step secondStep) {
return jobBuilderFactory.get("updateNotaryDistrictsJob")
.start(firstStep)
.next(secondStep)
//.next(playerSummarization())
.build();
}
@Bean
public ExecutionContext executionContext() {
return new ExecutionContext();
}
/**
************************************ UPDATE NOTARY LIST JOB SECTION ********************************************
*/
@Bean()
public ItemReaderAdapter serviceItemReader() {
ItemReaderAdapter reader = new ItemReaderAdapter();
reader.setTargetObject(notaryListServiceAdapter);
reader.setTargetMethod("nextNotaryElement");
return reader;
}
@Bean
public Step readNotaryListStep(){
return steps.get("readNotaryListStep").
<Integer,Integer>chunk(1)
.reader(serviceItemReader())
.processor(new NotaryDetailsEnrichProcessor(notaryService))
.writer(new ConsoleItemWriter())
.build();
}
@Bean("updateNotaryListInfoJob")
public Job updateNotaryListInfoJob(){
return jobs.get("updateNotaryListInfoJob")
.incrementer(new RunIdIncrementer())
.start(readNotaryListStep())
.build();
}
}
然后,我第一时间创建了另一个 SpringBatchExampleJobLauncher 启动器 class。这工作正常,最初用于启动单个作业(我想我必须更改此启动器的逻辑 class 以执行两个作业而不是一个作业):
public class SpringBatchExampleJobLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBatchExampleJobLauncher.class);
private final Job job;
private final JobLauncher jobLauncher;
private ExecutionContext executionContext;
@Autowired
public SpringBatchExampleJobLauncher(@Qualifier("updateNotaryDistrictsJob") Job job,
JobLauncher jobLauncher,
ExecutionContext executionContext) {
this.job = job;
this.jobLauncher = jobLauncher;
this.executionContext = executionContext;
}
//@Scheduled(cron = "0 */3 * * * *")
@Scheduled(cron = "0/30 * * * * *")
public void runSpringBatchExampleJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
LOGGER.info("Spring Batch example job was started");
List<NotaryDistrict> notaryDistrictsList = new ArrayList<NotaryDistrict>();
executionContext.put("notaryDistrictsList", notaryDistrictsList);
jobLauncher.run(job, newExecution());
LOGGER.info("Spring Batch example job was stopped");
}
private JobParameters newExecution() {
Map<String, JobParameter> parameters = new HashMap<>();
JobParameter parameter = new JobParameter(new Date());
parameters.put("currentTime", parameter);
return new JobParameters(parameters);
}
}
如您所见,这个 class 非常简单:它的构造函数执行一个特定的作业(由定义到先前配置中的 @Qualifier 标识 class), JobLauncher 以便 运行 dis 作业和 ExecutionContext.
然后它包含 运行SpringBatchExampleJob() 即 运行 这个作业每 30 秒一次(由 CRON 指定例外)。
好的....因此,为了开始我的 2 个工作,我认为我需要以与所示类似的方式更改此 SpringBatchExampleJobLauncher这里:https://newbedev.com/spring-batch-running-multiple-jobs-in-parallel
所以我做了什么。首先,我将 ThreadPoolTaskExecutor 和 JobLauncher bean 定义添加到我的 UpdateInfoBatchConfig 配置中 class,变成这样:
@Configuration
public class UpdateInfoBatchConfig {
private static final String PROPERTY_REST_API_URL = "rest.api.url";
@Autowired
private NotaryListServiceAdapter notaryListServiceAdapter;
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private NotaryService notaryService;
@Bean("firstStepItemReader")
public ItemReader<NotaryDistrict> itemReader(Environment environment, RestTemplate restTemplate) throws IllegalStateException, URISyntaxException {
System.out.println("itemReader() START !!!");
return new RESTNotaryDistrictsReader();
}
@Bean("firstStepItemWriter")
public ItemWriter<NotaryDistrict> itemWriter() {
return new LoggingItemWriter();
}
@Bean("secondStepItemReader")
public ItemReader<NotaryDistrict> secondStepReader(Environment environment) throws IllegalStateException {
System.out.println("secondStepItemReader() creation !!!");
return new SecondStepItemReader();
}
@Bean("secondStepItemProcessor")
public ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor() {
return new SecondStepItemProcessor();
}
@Bean("secondStepItemWriter")
public ItemWriter<NotaryDistrict> secondStepItemWriter() {
return new SecondStepItemWriter();
}
/**
* Creates a bean that represents the first step of the batch.
* How it works:
* 1) Call an external API in order to retrieve notary districts list
* 2) Return notary district one by one to the second step
* @param reader a custom reader calling an external API
* @param writer
* @param stepBuilderFactory
* @return
*/
@Bean("firstStep")
public Step firstStep(@Qualifier("firstStepItemReader") ItemReader<NotaryDistrict> reader,
@Qualifier("firstStepItemWriter") ItemWriter<NotaryDistrict> writer,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("updateNotaryDistrictsStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(reader)
.writer(writer)
.build();
}
@Bean("secondStep")
public Step secondStep(@Qualifier("secondStepItemReader") ItemReader<NotaryDistrict> secondStepItemReader,
@Qualifier("secondStepItemProcessor") ItemProcessor<NotaryDistrict, NotaryDistrict> secondStepItemProcessor,
@Qualifier("secondStepItemWriter") ItemWriter<NotaryDistrict> secondStepItemWriter,
StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("secondStep")
.<NotaryDistrict, NotaryDistrict>chunk(1)
.reader(secondStepItemReader)
.processor(secondStepItemProcessor)
.writer(secondStepItemWriter)
.build();
}
@Bean("updateNotaryDistrictsJob")
public Job updateNotaryDistrictsJob(JobBuilderFactory jobBuilderFactory,
@Qualifier("firstStep") Step firstStep,
@Qualifier("secondStep") Step secondStep) {
return jobBuilderFactory.get("updateNotaryDistrictsJob")
.start(firstStep)
.next(secondStep)
//.next(playerSummarization())
.build();
}
@Bean
public ExecutionContext executionContext() {
return new ExecutionContext();
}
/**
************************************ UPDATE NOTARY LIST JOB ********************************************
*/
@Bean()
public ItemReaderAdapter serviceItemReader() {
ItemReaderAdapter reader = new ItemReaderAdapter();
reader.setTargetObject(notaryListServiceAdapter);
reader.setTargetMethod("nextNotaryElement");
return reader;
}
@Bean
public Step readNotaryListStep(){
return steps.get("readNotaryListStep").
<Integer,Integer>chunk(1)
.reader(serviceItemReader())
.processor(new NotaryDetailsEnrichProcessor(notaryService))
.writer(new ConsoleItemWriter())
.build();
}
@Bean("updateNotaryListInfoJob")
public Job updateNotaryListInfoJob(){
return jobs.get("updateNotaryListInfoJob")
.incrementer(new RunIdIncrementer())
.start(readNotaryListStep())
.build();
}
/**
************************************ MULTIPLE JOB CONFIGURATION ********************************************
*/
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(15);
taskExecutor.setMaxPoolSize(20);
taskExecutor.setQueueCapacity(30);
return taskExecutor;
}
@Bean
public JobLauncher jobLauncher(ThreadPoolTaskExecutor taskExecutor, JobRepository jobRepository){
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(taskExecutor);
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
}
如您所见,最后两个 bean 是 ThreadPoolTaskExecutor 和我的 JobLauncher bean。
然后我更改了 SpringBatchExampleJobLauncher 以便使用此启动器并执行我的两个作业而不是一个作业,这就是我所做的:
/**
* This bean schedules and runs our Spring Batch job.
*/
@Component
public class SpringBatchExampleJobLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBatchExampleJobLauncher.class);
@Autowired
private JobLauncher jobLauncher;
@Autowired
@Qualifier("updateNotaryDistrictsJob")
private Job updateNotaryDistrictsJob;
@Autowired
@Qualifier("updateNotaryListInfoJob")
private Job updateNotaryListInfoJob;
@Scheduled(cron = "0/30 * * * * *")
public void run1(){
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(updateNotaryDistrictsJob, jobParameters);
}catch (Exception ex){
LOGGER.error(ex.getMessage());
}
}
@Scheduled(cron = "0/50 * * * * *")
public void run2(){
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(updateNotaryListInfoJob, jobParameters);
}catch (Exception ex){
LOGGER.error(ex.getMessage());
}
}
}
如您所见,我现在正在注入之前定义的 JobLauncher bean 和我的两个作业 bean(在我的配置 class 中定义)。然后我定义了 运行1() 和应该 运行 的 运行2() 方法当满足 CRON 表达式时,我的两个注入作业。
问题是我现在在我的 stracktrace 中收到以下错误并且没有执行任何操作:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'jobLauncher', defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/notariato/updateInfo/UpdateInfoBatchConfig.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
基本上,在我看来,这个错误是在告诉我,我试图注入我的启动器 class 的 JobLauncher bean 尚未定义到我的 UpdateInfoBatchConfig 配置 class。但这正是我所期望的,因为我将我的 bean 定义到配置 class 中,然后我将它注入到要使用的启动器 class 中。
怎么了?我错过了什么?我该如何尝试解决这个问题?
这是因为您在应用程序上下文中定义了一个 JobLauncher
bean,并且 Spring Batch 也通过 @EnableBatchProcessing
(参见它的 Javadoc)定义了那个 bean。
如果您想使用自定义 JobLauncher
,您应该提供一个 BatchConfigurer
bean 并覆盖 getJobLauncher
。一种方法是使您的配置 类 扩展 DefaultBatchConfigurer
并覆盖 createJobLauncher()
。这在文档 here.