如何正确定义两个不同的 Spring 批处理作业 bean 并将其注入 class 以 运行 这些作业?

How to correctly define and inject two different Spring Batch Job beans into the class that will run these jobs?

我正在学习Spring批量完成Spring按照本教程引导: https://www.petrikainulainen.net/programming/spring-framework/spring-batch-tutorial-reading-information-from-a-rest-api/

这里是相关的GitHub项目:https://github.com/pkainulainen/spring-batch-examples/tree/master/reading-data/rest-api

我在尝试理解什么是确切的执行流程以及如何修改它以实现我的自定义行为时发现了一些困难。我试着在这里解释我的疑惑:

此项目包含 SpringBatchExampleJobConfig 配置 class,其中定义了 Spring bean 工厂使用的 bean。这些 beans 应该被注入到其他 classes(是否正确?):

@Configuration
public class SpringBatchExampleJobConfig {

    private static final String PROPERTY_REST_API_URL = "rest.api.url";

    @Bean
    public ItemReader<StudentDTO> itemReader(Environment environment, RestTemplate restTemplate) {
        return new RESTStudentReader(environment.getRequiredProperty(PROPERTY_REST_API_URL), restTemplate);
    }

    @Bean
    public ItemWriter<StudentDTO> itemWriter() {
        return new LoggingItemWriter();
    }

    /**
     * Creates a bean that represents the only step of our batch job.
     * @param reader
     * @param writer
     * @param stepBuilderFactory
     * @return
     */
    @Bean
    public Step exampleJobStep(ItemReader<StudentDTO> reader,
                               ItemWriter<StudentDTO> writer,
                               StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("exampleJobStep")
                .<StudentDTO, StudentDTO>chunk(1)
                .reader(reader)
                .writer(writer)
                .build();
    }

    /**
     * Creates a bean that represents our example batch job.
     * @param exampleJobStep
     * @param jobBuilderFactory
     * @return
     */
    @Bean
    public Job exampleJob(Step exampleJobStep,
                          JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("exampleJob")
                .incrementer(new RunIdIncrementer())
                .flow(exampleJobStep)
                .end()
                .build();
    }
}

基本上它定义了 itemReader bean(从源读取数据),itemWriter bean(将数据写入目的地)。

然后它正在创建 exampleJobStep bean 来定义这个作业的步骤:基本上步骤是:从源读取并将数据传递给将写入的编写器目的地。正确吗?

最后它正在创建 exampleJob bean,这个:

@Bean
public Job exampleJob(Step exampleJobStep,
                      JobBuilderFactory jobBuilderFactory) {
    return jobBuilderFactory.get("exampleJob")
            .incrementer(new RunIdIncrementer())
            .flow(exampleJobStep)
            .end()
            .build();
}

它应该简单地执行由 exampleJobStep bean 定义的步骤。

到目前为止我的理解是否正确?

我的疑问是:作业开始的位置和方式(我的意思是之前的 exampleJob bean 表示的作业)。

在 GitHube 代码中,您可以找到 SpringBatchExampleJobLauncher,我认为这是执行作业的地方。基本上它是将 Job 对象自动装配到构造函数中:

@Autowired
public SpringBatchExampleJobLauncher(Job job, JobLauncher jobLauncher) {
    this.job = job;
    this.jobLauncher = jobLauncher;
}

我的疑问是:它是按类型自动装配的吗?所以这意味着在 beans 配置中定义这样的 bean class:

@Bean
public Job exampleJob(Step exampleJobStep,
                      JobBuilderFactory jobBuilderFactory) {
    return jobBuilderFactory.get("exampleJob")
            .incrementer(new RunIdIncrementer())
            .flow(exampleJobStep)
            .end()
            .build();
}

这是将自动装配到之前 SpringBatchExampleJobLauncher class 的构造函数中的 bean,我的工作是从哪里开始的?

如果我已经很好地理解它是如何工作的。假设现在我需要实现两个不同的作业并且我想同时启动这两个作业,我该怎么做才能实现这种行为?

目前我的 SpringBatchExampleJobLauncher class 包含

@Autowired
public SpringBatchExampleJobLauncher(Job job, JobLauncher jobLauncher) {
    this.job = job;
    this.jobLauncher = jobLauncher;
}

@Scheduled(cron = "0/10 * * * * *")
public void runSpringBatchExampleJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
    LOGGER.info("Spring Batch example job was started");

    jobLauncher.run(job, newExecution());

    LOGGER.info("Spring Batch example job was stopped");
}

它自动装配(我怀疑是类型)我唯一的 Job 对象,比这个作业是由 运行SpringBatchExampleJob 方法。

好的...我的想法是将两个不同的 bean 定义到我的 UpdateInfoBatchConfig class 中。但是万一我能做些什么来定义两个具有 Job 类型的不同 bean?以及如何正确地将这两个不同的作业 bean 注入我的 SpringBatchExampleJobLauncher class?

解决问题的方法不止一种。最方便的是用Spring引导。然后,您可以将任意数量的作业 bean 添加到应用程序上下文,并且 Spring Boot 将在应用程序启动时启动所有作业 bean,如果您使用参数 spring.batch.job.names,则仅启动其中的一个子集。请查看官方文档是否符合您的需求:https://docs.spring.io/spring-boot/docs/2.5.3/reference/html/howto.html#howto.batch

最直接的方法是使用 bean 名称作为限定符。默认情况下,bean 带有生成它们的方法的名称,但您也可以显式设置它。例如,以下代码草图生成两个名为 jobOnejobTwo

的作业 bean
@Bean
Job jobOne() {
  return jobBuilderFactory
    ...
    .build();
}

@Bean("jobTwo") {
Job aMethodNameWithoutInfluenceOnTheBeanName() {
  return jobBuilderFactory
    ...
    .build();
}

然后您可以使用 bean 名称来限定要自动连接的作业 bean:

@Autowired
public SpringBatchExampleJobLauncher(
  @Qualifier("jobOne") Job job1,
  @Qualifier("jobTwo") Job job2,
  JobLauncher jobLauncher
) {
  this.job1 = job1;
  this.job2 = job2;
  this.jobLauncher = jobLauncher;
}

同样的技巧允许您 select 哪些步骤应该自动连接到哪个作业 bean 方法。