在前面的例子中,我们配置的任务都是在项目启动的时候自动运行,我们也可以通过JobLauncher或者JobOperator手动控制任务的运行时机,这节记录下它们的用法。

框架搭建

新建一个Spring Boot项目,版本为2.2.4.RELEASE,artifactId为spring-batch-launcher,项目结构如下图所示:

Spring Batch任务调度 - 图1

剩下的数据库层的准备,项目配置,依赖引入和Spring Batch入门文章中的框架搭建步骤一致,这里就不再赘述。

此外,本节我们需要演示在Controller里通过JobLauncher或者JobOperator调度任务,所以我们还需在pom里引入web依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>

然后准备个任务,用于后续测试。在cc.mrbird.batch包下新建job包,然后在该包下新建MyJob

  1. @Component
  2. public class MyJob{
  3. @Autowired
  4. private JobBuilderFactory jobBuilderFactory;
  5. @Autowired
  6. private StepBuilderFactory stepBuilderFactory;
  7. @Bean
  8. public Job job(){
  9. return jobBuilderFactory.get("job")
  10. .start(step())
  11. .build();
  12. }
  13. private Step step(){
  14. return stepBuilderFactory.get("step")
  15. .tasklet((stepContribution, chunkContext) -> {
  16. StepExecution stepExecution = chunkContext.getStepContext().getStepExecution();
  17. Map<String, JobParameter> parameters = stepExecution.getJobParameters().getParameters();
  18. System.out.println(parameters.get("message").getValue());
  19. return RepeatStatus.FINISHED;
  20. })
  21. .listener(this)
  22. .build();
  23. }
  24. }

step()方法中,我们通过执行上下文获取了key为message的参数值。

JobLauncher

在cc.mrbird.batch包下新建controller包,然后在该包下新建JobController

  1. @RestController
  2. @RequestMapping("job")
  3. public class JobController {
  4. @Autowired
  5. private Job job;
  6. @Autowired
  7. private JobLauncher jobLauncher;
  8. @GetMapping("launcher/{message}")
  9. public String launcher(@PathVariable String message) throws Exception {
  10. JobParameters parameters = new JobParametersBuilder()
  11. .addString("message", message)
  12. .toJobParameters();
  13. // 将参数传递给任务
  14. jobLauncher.run(job, parameters);
  15. return "success";
  16. }
  17. }

上面代码中,我们注入了JobLauncher和上面配置的Job,然后通过JobLauncherrun(Job job, JobParameters jobParameters)方法运行指定的任务Job,并且传递了参数。

要关闭Spring Batch启动项目自动运行任务的机制,需要在项目配置文件application.yml中添加如下配置:

  1. spring:
  2. batch:
  3. job:
  4. enabled: false

启动项目,在浏览器地址栏访问:http://localhost:8080/job/launcher/hello:

Spring Batch任务调度 - 图2

项目控制台日志打印如下:

  1. 2020-03-12 10:24:31.547 INFO 41266 --- [nio-8080-exec-4] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=job]] launched with the following parameters: [{message=hello}]
  2. 2020-03-12 10:24:31.583 INFO 41266 --- [nio-8080-exec-4] o.s.batch.core.job.SimpleStepHandler : Executing step: [step]
  3. hello
  4. 2020-03-12 10:24:31.610 INFO 41266 --- [nio-8080-exec-4] o.s.batch.core.step.AbstractStep : Step: [step] executed in 27ms
  5. 2020-03-12 10:24:31.632 INFO 41266 --- [nio-8080-exec-4] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=job]] completed with the following parameters: [{message=hello}] and the following status: [COMPLETED] in 76ms

此外,需要注意的是:同样的参数,同样的任务再次运行的时候将抛出JobInstanceAlreadyCompleteException异常,比如在浏览器中再次访问http://localhost:8080/job/launcher/hello,项目控制台日志打印如下:

  1. org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={message=hello}. If you want to run this job again, change the parameters.
  2. at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:131) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
  3. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_231]
  4. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_231]
  5. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_231]
  6. at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_231]
  7. at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.4.RELEASE.jar:5.2.4.RELEASE]
  8. ...

所以我们在任务调度的时候,应避免参数重复。

JobOperator

JobController里添加一个新的端点:

  1. @RestController
  2. @RequestMapping("job")
  3. public class JobController {
  4. @Autowired
  5. private JobOperator jobOperator;
  6. @GetMapping("operator/{message}")
  7. public String operator(@PathVariable String message) throws Exception {
  8. // 传递任务名称,参数使用 kv方式
  9. jobOperator.start("job", "message=" + message);
  10. return "success";
  11. }
  12. }

上面代码中,我们注入了JobOperatorJobOperatorstart(String jobName, String parameters)方法传入的是任务的名称(任务在Spring IOC容器中的名称),并且参数使用key-value的方式传递。

要通过任务名称获取到相应的Bean,还需要添加一个额外的配置。在cc.mrbird.batch包下新建configure包,然后在该包下新建JobConfigure

  1. @Configuration
  2. public class JobConfigure {
  3. /**
  4. * 注册JobRegistryBeanPostProcessor bean
  5. * 用于将任务名称和实际的任务关联起来
  6. */
  7. @Bean
  8. public JobRegistryBeanPostProcessor processor(JobRegistry jobRegistry, ApplicationContext applicationContext) {
  9. JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
  10. postProcessor.setJobRegistry(jobRegistry);
  11. postProcessor.setBeanFactory(applicationContext.getAutowireCapableBeanFactory());
  12. return postProcessor;
  13. }
  14. }

如果没有这段配置,在任务调度的时候将报org.springframework.batch.core.launch.NoSuchJobException: No job configuration with the name [job] was registered。

启动任务,浏览器访问:http://localhost:8080/job/operator/mrbird:

Spring Batch任务调度 - 图3

项目控制台日志打印如下:

  1. 2020-03-12 10:51:20.174 INFO 41405 --- [nio-8080-exec-2] o.s.b.c.l.support.SimpleJobOperator : Checking status of job with name=job
  2. 2020-03-12 10:51:20.183 INFO 41405 --- [nio-8080-exec-2] o.s.b.c.l.support.SimpleJobOperator : Attempting to launch job with name=job and parameters=message=mrbird
  3. 2020-03-12 10:51:20.239 INFO 41405 --- [nio-8080-exec-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=job]] launched with the following parameters: [{message=mrbird}]
  4. 2020-03-12 10:51:20.293 INFO 41405 --- [nio-8080-exec-2] o.s.batch.core.job.SimpleStepHandler : Executing step: [step]
  5. mrbird
  6. 2020-03-12 10:51:20.324 INFO 41405 --- [nio-8080-exec-2] o.s.batch.core.step.AbstractStep : Step: [step] executed in 31ms
  7. 2020-03-12 10:51:20.344 INFO 41405 --- [nio-8080-exec-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=job]] completed with the following parameters: [{message=mrbird}] and the following status: [COMPLETED] in 83ms

JobOperator包含了许多实用的方法:

Spring Batch任务调度 - 图4

具体可以自己尝试玩一玩。