java 如何按特定顺序运行 Spring Batch 作业(Spring Boot)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41364220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:50:01  来源:igfitidea点击:

How to run Spring Batch Jobs in certain order (Spring Boot)?

javaspringspring-bootspring-batch

提问by Gerard Bosch

I'm developing with Spring Batch using Spring Boot.

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

I'm with the minimal configuration provided by Spring Boot and defined some Jobs (no XML configuration at all). But when I run the application,

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

SpringApplication.run(App.class, args);

the jobs are sequentially executed in some arbitrary order.

作业以某种任意顺序依次执行。

I'm defining the jobs this way in @Configurationannotated classes, Spring do the rest:

我在带@Configuration注释的类中以这种方式定义工作,Spring 完成剩下的工作:

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

How can I instruct the framework to run the jobs in a certain order?

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

EDIT: Could this warning give a hint? (Maybe has nothing to be)

编辑:此警告可以提供提示吗?(也许没什么可说的)

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

回答by Sabir Khan

1.You first disable automatic job start by specifying spring.batch.job.enabled=falsein application.properties

1.您首先通过spring.batch.job.enabled=falseapplication.properties 中指定来禁用自动作业启动

2.In your main class, do - ApplicationContext ctx = SpringApplication.run(SpringBatchMain.class, args);assuming your main class is named - SpringBatchMain.java.

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

This will initialize context without starting any jobs.

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

3.Once context is initialized, either you can do - JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");or do Autowiredfor this JobLauncher bean in main class and launch specific jobs sequentially in specific sequential order by invoking , jobLauncher.run(job, jobParameters).

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

You can get specific jobinstances from context initialized at step # 2.

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

You can always use any ordered collection to put your jobs there and launch jobs by iterating over that collection.

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

4.This above technique works as long as your JobLauncher is configured to be synchronous i.e. main thread waits for jobLauncher.run()call to complete and that is default behavior of jobLauncher.

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

If you have defined your jobLauncher to use AsyncTaskExecutor then jobs will be started in parallel and sequential ordering will not be maintained.

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

Hope it helps !!

希望能帮助到你 !!

EDIT:

编辑:

I was experimenting with @Orderannotation as pointed by Stephane Nicoll and it seems to help only in creating an Ordered collection of jobs and that you can iterate and launch jobs in that order.

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

This below component gives me jobs in Order specified ,

下面的组件按指定的顺序为我提供了工作,

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

    public List<Job> getJobs() {
        return jobs;
    }
}

and I can do , MyJobs myJobs = (MyJobs) ctx.getBean("myJobs");in main class provided bean is defined,

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

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

I can iterate over myJobsand launch jobs in that order as specified by @Order annotation.

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

回答by Stephane Nicoll

Order them.

订购它们。

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

See the javadoc of @Orderfor more details.

有关更多详细信息,请参阅的javadoc@Order

回答by loonis

Here is an illustration of the solution.

这是解决方案的说明。

This is so weird, it looks like we're hacking the process.

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

spring.batch.job.enabled=false

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());
    }

}

回答by William Lee

I don't have enough rep to comment. But have you tried just to manually launch your jobs in the order you want?

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

You need to set spring.batch.job.enabled=falsein your application.properties, so that your jobs are not run automatically.

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

Then just use a launcher to launch your jobs in the order you want.

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

@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());
    }

}

Here is the output

这是输出

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: ...

回答by Chandu

if your one job is dependent on the second and so on, then do something like this.

如果你的一份工作依赖于第二份等等,那么做这样的事情。

@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();
    }
}