如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?

Posted

技术标签:

【中文标题】如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?【英文标题】:How to run Spring Batch Jobs in certain order (Spring Boot)? 【发布时间】:2017-05-12 20:45:30 【问题描述】:

我正在使用 Spring Boot 使用 Spring Batch 进行开发。

我使用 Spring Boot 提供的最小配置并定义了一些作业(根本没有 XML 配置)。但是当我运行应用程序时,

SpringApplication.run(App.class, args);

作业以任意顺序顺序执行。

我在 @Configuration 带注释的类中以这种方式定义作业,其余的由 Spring 完成:

@Bean
public Job requestTickets() 
    return jobBuilderFactory.get(Config.JOB_REQUEST_TICKETS)
            .start(stepRequestTickets())
            .build();

如何指示框架按特定顺序运行作业?

编辑:这个警告能给出提示吗? (也许没什么)

2016-12-29 17:45:33.320  WARN 3528 --- [main] o.s.b.c.c.a.DefaultBatchConfigurer: No datasource was provided...using a Map based JobRepository

【问题讨论】:

所以你有一份工作有多个步骤或多个工作?好像,你只有一份工作。当您说 jobs 时,您是指 steps 吗?显示stepRequestTickets() 的代码。 我有多个工作。我想按固定顺序依次运行作业,但找不到订购作业的方法。这只是我如何定义 Job 的一个 sn-p。 【参考方案1】:

1.您首先通过在 application.properties

中指定 spring.batch.job.enabled=false 来禁用自动作业启动

2.在你的主类中,做 - ApplicationContext ctx = SpringApplication.run(SpringBatchMain.class, args); 假设你的主类被命名为 - SpringBatchMain.java。

这将在不启动任何作业的情况下初始化上下文。

3.一旦上下文被初始化,你可以做 - JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher"); 或在主类中为这个 JobLauncher bean 做 Autowired 并通过调用 jobLauncher.run(job, jobParameters) 以特定顺序依次启动特定作业。

您可以从第 2 步初始化的上下文中获取特定的 job 实例。

您始终可以使用任何有序集合将您的作业放在那里,并通过迭代该集合来启动作业。

4. 只要您的 JobLauncher 配置为同步,即主线程等待 jobLauncher.run() 调用完成,并且这是 jobLauncher 的默认行为,上述技术就可以工作。

如果您已将 jobLauncher 定义为使用 AsyncTaskExecutor,则作业将并行启动,并且不会保持顺序排序。

希望对你有帮助!!

编辑:

我正在试验 Stephane Nicoll 指出的 @Order 注释,它似乎只有助于创建有序的作业集合,并且您可以按该顺序迭代和启动作业。

这个下面的组件给了我指定顺序的工作,

@Component
public class MyJobs 
    @Autowired
    private List<Job> jobs;

    public List<Job> getJobs() 
        return jobs;
    

我可以做到,MyJobs myJobs = (MyJobs) ctx.getBean("myJobs"); 在主类中提供 bean 已定义,

@Bean
    public MyJobs myJobs() 
        return new MyJobs();
    

我可以遍历myJobs 并按照@Order 注释指定的顺序启动作业。

【讨论】:

成功了!但是一个额外的问题:您将@Beancode 放在哪里?如果我将它放在main 方法中,它工作正常。但是如果我把它放在 @Component 里面,我会得到一个 Spring 错误:BeanDefinitionStoreException: Invalid bean definition with name 'myJobs' defined in class path resource [.../MyJobs.class]: factory-bean reference points back to the same bean definition 我发现@Bean 的定义是不必要的。我猜@Component 已经创建了所需的 bean。 我的意思是,如果您的工作太多且难以管理,则需要使用 Order。对于一两个作业,您可以简单地手动启动作业,而无需使用订单注释。 我使用@Order 管理它并遍历作业列表以运行作业。但我不需要声明@Bean myJobs() 代码。我可以在没有声明 @Bean 的情况下制作 ctx.getBean(MyJobs.class).getJobs(),只需使用 @Component 部分。谢谢让它工作:)【参考方案2】:

订购它们。

@Bean
@Order(42)
public Job requestTickets() 
    return jobBuilderFactory.get(Config.JOB_REQUEST_TICKETS)
            .start(stepRequestTickets())
            .build();

有关详细信息,请参阅javadoc of @Order

【讨论】:

@Order 没有成功。我完全按照您的建议将注释插入了 @Order(1)@Order(2)@Order(3) 到我的 3 个作业中,但它们的执行方式与以前一样。 那么您没有使用作业启动器。或者您使用的是非常旧的 Spring Boot 版本。 @StephaneNic​​oll:那你是什么意思,那么你没有使用作业启动器jobLauncher.run(job, jobParameters) 运行特定的作业,所以它就像手动订购一样。我也测试了@Order,但它没有按照您的回答中描述的那样工作。我正在使用 Spring Boot 1.4.0。不知道 OP 有没有更新? 对我来说,无论 Job bean 首先在 @Configuration 类中定义,无论 Order 是否指定,都会首先执行,【参考方案3】:

这是解决方案的说明。

这太奇怪了,看起来我们正在破解进程。

spring.batch.job.enabled=false

@SpringBootApplication
@EnableBatchProcessing
public class MyApplication 

    public static void main(String[] args)
            throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException 

        ConfigurableApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
        Job job1= (Job) ctx.getBean("job1");
        Job job2= (Job) ctx.getBean("job2");
        jobLauncher.run(job1,new JobParameters());
        jobLauncher.run(job2,new JobParameters());
    


【讨论】:

【参考方案4】:

我没有足够的代表发表评论。但是您是否尝试过按照您想要的顺序手动启动您的工作?

您需要在 application.properties 中设置 spring.batch.job.enabled=false,以便您的作业不会自动运行。

然后只需使用启动器按您想要的顺序启动您的作业。

@RunWith(SpringRunner.class)
@SpringBootTest(classes =  TestConfiguration.class, TestDataSourceConfiguration.class, TestBatchConfig.class )
public class JobOrderTest 

    @Autowired
    JobLauncher jobLauncher;

    @Mock
    Job firstJob;

    @Mock
    Job secondJob;

    @Mock
    Job thirdJob;

    @Mock
    JobParametersValidator jobParametersValidator;

    @Test
    public void jobInOrderTest() throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException 

        when(firstJob.getName()).thenReturn(UUID.randomUUID().toString());
        when(secondJob.getName()).thenReturn(UUID.randomUUID().toString());
        when(thirdJob.getName()).thenReturn(UUID.randomUUID().toString());
        when(firstJob.getJobParametersValidator()).thenReturn(jobParametersValidator);
        when(secondJob.getJobParametersValidator()).thenReturn(jobParametersValidator);
        when(thirdJob.getJobParametersValidator()).thenReturn(jobParametersValidator);

        jobLauncher.run(firstJob, new JobParameters());
        jobLauncher.run(secondJob, new JobParameters());
        jobLauncher.run(thirdJob, new JobParameters());
    


这是输出

2016-12-30 09:48:36.457  INFO 144860 --- [cTaskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [firstJob] launched with the following parameters: ...
2016-12-30 09:48:36.457  INFO 144860 --- [cTaskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [firstJob] completed with the following parameters: ...
2016-12-30 09:48:36.478  INFO 144860 --- [cTaskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher      : Job: [secondJob] launched with the following parameters: ...
2016-12-30 09:48:36.478  INFO 144860 --- [cTaskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher      : Job: [secondJob] completed with the following parameters: ...
2016-12-30 09:48:36.508  INFO 144860 --- [cTaskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [thirdJob] launched with the following parameters: ...
2016-12-30 09:48:36.508  INFO 144860 --- [cTaskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [thirdJob] completed with the following parameters: ...

【讨论】:

【参考方案5】:

如果您的一项工作依赖于第二项工作,依此类推,请执行以下操作。

@Configuration
@EnableBatchProcessing
@Import(DataSourceConfiguration.class)
public class AppConfig 

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public Job job(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) 
        return jobs.get("myJob").start(step1).next(step2).build();
    

    @Bean
    protected Step step1(ItemReader<Person> reader, ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) 
        return steps.get("step1")
            .<Person, Person> chunk(10)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    

    @Bean
    protected Step step2(Tasklet tasklet) 
        return steps.get("step2")
            .tasklet(tasklet)
            .build();
    

【讨论】:

我认为您在谈论订购步骤,但我需要按特定顺序运行 JOBS,因为 Job_n 取决于 Job_n-1

以上是关于如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?的主要内容,如果未能解决你的问题,请参考以下文章

Java Spring Boot 加 Spring Batch 创建 Jar 并仅运行特定作业

如何让 Jest 按特定顺序运行测试?

如何按特定顺序运行 Maven 测试? [复制]

如何在 JUnit4 中按特定顺序运行测试方法?

如何使用spring集成测试按顺序运行控制器测试类

有没有办法只为特定作业跳过 Spring Batch 的持久元数据?