原文: https://howtodoinjava.com/spring-batch/batch-quartz-java-config-example/

学习配置Quartz调度程序以运行使用 Spring Boot Java 配置配置的 Spring Batch 作业。 尽管 Spring 的默认调度程序也不错,但是 Quartz 可以更好地并且以更可配置的方式更好地调度和调用任务。 这使 Spring Batch 仅专注于创建批处理作业,而让 Quartz 执行它们。

项目概述和结构

在此带有 Quartz 示例的 SpringBatch 中,我们将创建两个具有不同步骤数的不同批处理作业。 然后,我们将使用夸脱作业数据映射和触发器安排作业执行时间,并在控制台中观察输出。

Spring Batch Quartz Java 配置示例 - 图1

Spring Batch Quartz Java 配置示例

Maven 依赖

Spring Boot 具有对 Quartz 的内置支持,因此您所需要做的就是导入依赖项,例如spring-boot-starter-quartzspring-boot-starter-batch

请注意,quartz 需要至少一个数据库来存储作业执行详细信息。 在这个例子中,我使用的是 Spring Boot 支持的H2数据库。 因此也包括'h2'依赖项。

pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd;
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.howtodoinjava</groupId>
  6. <artifactId>App</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>App</name>
  10. <url>http://maven.apache.org</url>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>2.0.3.RELEASE</version>
  15. </parent>
  16. <properties>
  17. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-batch</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-quartz</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>com.h2database</groupId>
  30. <artifactId>h2</artifactId>
  31. <scope>runtime</scope>
  32. </dependency>
  33. </dependencies>
  34. <build>
  35. <plugins>
  36. <plugin>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-maven-plugin</artifactId>
  39. </plugin>
  40. </plugins>
  41. </build>
  42. <repositories>
  43. <repository>
  44. <id>repository.spring.release</id>
  45. <name>Spring GA Repository</name>
  46. <url>http://repo.spring.io/release</url>
  47. </repository>
  48. </repositories>
  49. </project>

创建具有多个任务的批处理作业

  1. 创建任务


首先创建几个小任务。 这些是在单个批处理作业中一步处理的策略。
MyTaskOne.java

  1. import org.springframework.batch.core.StepContribution;
  2. import org.springframework.batch.core.scope.context.ChunkContext;
  3. import org.springframework.batch.core.step.tasklet.Tasklet;
  4. import org.springframework.batch.repeat.RepeatStatus;
  5. public class MyTaskOne implements Tasklet {
  6. public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
  7. System.out.println("MyTaskOne start..");
  8. // ... your code
  9. System.out.println("MyTaskOne done..");
  10. return RepeatStatus.FINISHED;
  11. }
  12. }


MyTaskTwo.java

  1. public class MyTaskTwo implements Tasklet {
  2. public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
  3. System.out.println("MyTaskTwo start..");
  4. // ... your code
  5. System.out.println("MyTaskTwo done..");
  6. return RepeatStatus.FINISHED;
  7. }
  8. }
  1. 创建批处理作业


现在创建 SpringBatch,并在其中配置步骤。 我正在创建两个批处理作业。

  • demoJobOne具有 2 个步骤,即stepOnestepTwo
  • demoJobTwo和一个步骤,即stepOne

`BatchConfig.java````java import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;

import com.howtodoinjava.demo.tasks.MyTaskOne; import com.howtodoinjava.demo.tasks.MyTaskTwo;

@Configuration @EnableBatchProcessing public class BatchConfig {

  1. @Autowired
  2. private JobBuilderFactory jobs;
  3. @Autowired
  4. private StepBuilderFactory steps;
  5. @Bean
  6. public Step stepOne(){
  7. return steps.get("stepOne")
  8. .tasklet(new MyTaskOne())
  9. .build();
  10. }
  11. @Bean
  12. public Step stepTwo(){
  13. return steps.get("stepTwo")
  14. .tasklet(new MyTaskTwo())
  15. .build();
  16. }
  17. @Bean(name="demoJobOne")
  18. public Job demoJobOne(){
  19. return jobs.get("demoJobOne")
  20. .start(stepOne())
  21. .next(stepTwo())
  22. .build();
  23. }
  24. @Bean(name="demoJobTwo")
  25. public Job demoJobTwo(){
  26. return jobs.get("demoJobTwo")
  27. .flow(stepOne())
  28. .build()
  29. .build();
  30. }

}

  1. <a name="4e6ff5de"></a>
  2. ## 使用`QuartzJobBean`创建 Quartz 作业运行器
  3. 现在我们需要创建标准的`QuartzJobBean`实例,该实例将用于执行批处理作业。
  4. `CustomQuartzJob.java`
  5. ```java
  6. import org.quartz.JobExecutionContext;
  7. import org.quartz.JobExecutionException;
  8. import org.springframework.batch.core.Job;
  9. import org.springframework.batch.core.JobParameters;
  10. import org.springframework.batch.core.JobParametersBuilder;
  11. import org.springframework.batch.core.configuration.JobLocator;
  12. import org.springframework.batch.core.launch.JobLauncher;
  13. import org.springframework.scheduling.quartz.QuartzJobBean;
  14. public class CustomQuartzJob extends QuartzJobBean {
  15. private String jobName;
  16. private JobLauncher jobLauncher;
  17. private JobLocator jobLocator;
  18. public String getJobName() {
  19. return jobName;
  20. }
  21. public void setJobName(String jobName) {
  22. this.jobName = jobName;
  23. }
  24. public JobLauncher getJobLauncher() {
  25. return jobLauncher;
  26. }
  27. public void setJobLauncher(JobLauncher jobLauncher) {
  28. this.jobLauncher = jobLauncher;
  29. }
  30. public JobLocator getJobLocator() {
  31. return jobLocator;
  32. }
  33. public void setJobLocator(JobLocator jobLocator) {
  34. this.jobLocator = jobLocator;
  35. }
  36. @Override
  37. protected void executeInternal(JobExecutionContext context) throws JobExecutionException
  38. {
  39. try
  40. {
  41. Job job = jobLocator.getJob(jobName);
  42. JobParameters params = new JobParametersBuilder()
  43. .addString("JobID", String.valueOf(System.currentTimeMillis()))
  44. .toJobParameters();
  45. jobLauncher.run(job, params);
  46. }
  47. catch (Exception e)
  48. {
  49. e.printStackTrace();
  50. }
  51. }
  52. }

SchedulerFactoryBean中配置 Quartz 作业详细信息和触发器

  1. 现在可以配置 QuartzJobDetailtriggers,并将它们注册到 spring 的上下文中。
  2. JobDetail实例包含有关QuartzJobBean的信息和要使用JobDataMap注入的信息。
  3. 还创建Trigger实例以配置批处理作业的执行时间和频率。
  4. 最后,在SchedulerFactoryBean中添加作业详细信息和触发器,以配置 Spring 并管理其生命周期(作为 Spring 应用程序上下文的一部分)。 它会在初始化时自动启动调度程序,并在销毁时将其关闭。

QuartzConfig.java

  1. import java.io.IOException;
  2. import java.util.Properties;
  3. import org.quartz.JobBuilder;
  4. import org.quartz.JobDataMap;
  5. import org.quartz.JobDetail;
  6. import org.quartz.SimpleScheduleBuilder;
  7. import org.quartz.Trigger;
  8. import org.quartz.TriggerBuilder;
  9. import org.springframework.batch.core.configuration.JobLocator;
  10. import org.springframework.batch.core.configuration.JobRegistry;
  11. import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
  12. import org.springframework.batch.core.launch.JobLauncher;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.beans.factory.config.PropertiesFactoryBean;
  15. import org.springframework.context.annotation.Bean;
  16. import org.springframework.context.annotation.Configuration;
  17. import org.springframework.core.io.ClassPathResource;
  18. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  19. import com.howtodoinjava.demo.jobs.CustomQuartzJob;
  20. @Configuration
  21. public class QuartzConfig
  22. {
  23. @Autowired
  24. private JobLauncher jobLauncher;
  25. @Autowired
  26. private JobLocator jobLocator;
  27. @Bean
  28. public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
  29. JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
  30. jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
  31. return jobRegistryBeanPostProcessor;
  32. }
  33. @Bean
  34. public JobDetail jobOneDetail() {
  35. //Set Job data map
  36. JobDataMap jobDataMap = new JobDataMap();
  37. jobDataMap.put("jobName", "demoJobOne");
  38. jobDataMap.put("jobLauncher", jobLauncher);
  39. jobDataMap.put("jobLocator", jobLocator);
  40. return JobBuilder.newJob(CustomQuartzJob.class)
  41. .withIdentity("demoJobOne")
  42. .setJobData(jobDataMap)
  43. .storeDurably()
  44. .build();
  45. }
  46. @Bean
  47. public JobDetail jobTwoDetail() {
  48. //Set Job data map
  49. JobDataMap jobDataMap = new JobDataMap();
  50. jobDataMap.put("jobName", "demoJobTwo");
  51. jobDataMap.put("jobLauncher", jobLauncher);
  52. jobDataMap.put("jobLocator", jobLocator);
  53. return JobBuilder.newJob(CustomQuartzJob.class)
  54. .withIdentity("demoJobTwo")
  55. .setJobData(jobDataMap)
  56. .storeDurably()
  57. .build();
  58. }
  59. @Bean
  60. public Trigger jobOneTrigger()
  61. {
  62. SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder
  63. .simpleSchedule()
  64. .withIntervalInSeconds(10)
  65. .repeatForever();
  66. return TriggerBuilder
  67. .newTrigger()
  68. .forJob(jobOneDetail())
  69. .withIdentity("jobOneTrigger")
  70. .withSchedule(scheduleBuilder)
  71. .build();
  72. }
  73. @Bean
  74. public Trigger jobTwoTrigger()
  75. {
  76. SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder
  77. .simpleSchedule()
  78. .withIntervalInSeconds(20)
  79. .repeatForever();
  80. return TriggerBuilder
  81. .newTrigger()
  82. .forJob(jobTwoDetail())
  83. .withIdentity("jobTwoTrigger")
  84. .withSchedule(scheduleBuilder)
  85. .build();
  86. }
  87. @Bean
  88. public SchedulerFactoryBean schedulerFactoryBean() throws IOException
  89. {
  90. SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
  91. scheduler.setTriggers(jobOneTrigger(), jobTwoTrigger());
  92. scheduler.setQuartzProperties(quartzProperties());
  93. scheduler.setJobDetails(jobOneDetail(), jobTwoDetail());
  94. return scheduler;
  95. }
  96. @Bean
  97. public Properties quartzProperties() throws IOException
  98. {
  99. PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  100. propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
  101. propertiesFactoryBean.afterPropertiesSet();
  102. return propertiesFactoryBean.getObject();
  103. }
  104. }

使用属性文件自定义 Quartz

在 Quartz 中,您可以从简单的属性文件中控制很多东西。 例如

quartz.properties

  1. #scheduler name will be "MyScheduler"
  2. org.quartz.scheduler.instanceName = MyScheduler
  3. #maximum of 3 jobs can be run simultaneously
  4. org.quartz.threadPool.threadCount = 3
  5. #All of Quartz data is held in memory (rather than in a database).
  6. org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

示例

在运行应用程序之前,请确保已在application.properties文件中禁用了批处理作业自动启动功能。

application.properties

  1. #Disable batch job's auto start
  2. spring.batch.job.enabled=false
  3. #enable h2 databse console
  4. spring.h2.console.enabled=true

现在,将该应用程序作为 Spring Batch 应用程序运行,并检查日志。

App.java

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. @SpringBootApplication
  4. public class App
  5. {
  6. public static void main(String[] args)
  7. {
  8. SpringApplication.run(App.class, args);
  9. }
  10. }

Console

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: (v2.0.3.RELEASE)
  8. 2018-07-05 10:50:53 INFO - Starting App on FFC15B4E9C5AA with PID 9740 (C:\Users\zkpkhua\DigitalDashboard_Integrated\App\target\classes started by zkpkhua in C:\Users\zkpkhua\DigitalDashboard_Integrated\App)
  9. 2018-07-05 10:50:53 INFO - No active profile set, falling back to default profiles: default
  10. 2018-07-05 10:50:53 INFO - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@78b1cc93: startup date [Thu Jul 05 10:50:53 IST 2018]; root of context hierarchy
  11. 2018-07-05 10:50:54 INFO - Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  12. 2018-07-05 10:50:54 INFO - Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$EnhancerBySpringCGLIB$169797f6] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  13. 2018-07-05 10:50:54 INFO - Bean 'org.springframework.boot.context.properties.ConversionServiceDeducer$Factory' of type [org.springframework.boot.context.properties.ConversionServiceDeducer$Factory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  14. 2018-07-05 10:50:54 INFO - Bean 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  15. 2018-07-05 10:50:54 INFO - Bean 'dataSource' of type [com.zaxxer.hikari.HikariDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  16. 2018-07-05 10:50:54 INFO - Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  17. 2018-07-05 10:50:54 INFO - Bean 'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration' of type [org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$EnhancerBySpringCGLIB$165a725c] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  18. 2018-07-05 10:50:54 INFO - Bean 'jobLauncher' of type [com.sun.proxy.$Proxy41] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  19. 2018-07-05 10:50:54 INFO - Bean 'jobRegistry' of type [com.sun.proxy.$Proxy43] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  20. 2018-07-05 10:50:54 INFO - Bean 'quartzConfig' of type [com.howtodoinjava.demo.config.QuartzConfig$EnhancerBySpringCGLIB$7dc0f057] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
  21. 2018-07-05 10:50:55 INFO - HikariPool-1 - Starting...
  22. 2018-07-05 10:50:55 INFO - HikariPool-1 - Start completed.
  23. 2018-07-05 10:50:55 INFO - No database type set, using meta data indicating: H2
  24. 2018-07-05 10:50:55 INFO - No TaskExecutor has been set, defaulting to synchronous executor.
  25. 2018-07-05 10:50:56 INFO - Executing SQL script from class path resource [org/quartz/impl/jdbcjobstore/tables_h2.sql]
  26. 2018-07-05 10:50:56 INFO - Executed SQL script from class path resource [org/quartz/impl/jdbcjobstore/tables_h2.sql] in 69 ms.
  27. 2018-07-05 10:50:56 INFO - Using default implementation for ThreadExecutor
  28. 2018-07-05 10:50:56 INFO - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
  29. 2018-07-05 10:50:56 INFO - Quartz Scheduler v.2.3.0 created.
  30. 2018-07-05 10:50:56 INFO - RAMJobStore initialized.
  31. 2018-07-05 10:50:56 INFO - Scheduler meta-data: Quartz Scheduler (v2.3.0) 'schedulerFactoryBean' with instanceId 'NON_CLUSTERED'
  32. Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  33. NOT STARTED.
  34. Currently in standby mode.
  35. Number of jobs executed: 0
  36. Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
  37. Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
  38. 2018-07-05 10:50:56 INFO - Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
  39. 2018-07-05 10:50:56 INFO - Quartz scheduler version: 2.3.0
  40. 2018-07-05 10:50:56 INFO - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@1e7f2e0f
  41. 2018-07-05 10:50:56 INFO - Executing SQL script from class path resource [org/springframework/batch/core/schema-h2.sql]
  42. 2018-07-05 10:50:56 INFO - Executed SQL script from class path resource [org/springframework/batch/core/schema-h2.sql] in 30 ms.
  43. 2018-07-05 10:50:57 INFO - Registering beans for JMX exposure on startup
  44. 2018-07-05 10:50:57 INFO - Bean with name 'dataSource' has been autodetected for JMX exposure
  45. 2018-07-05 10:50:57 INFO - Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
  46. 2018-07-05 10:50:57 INFO - Starting beans in phase 2147483647
  47. 2018-07-05 10:50:57 INFO - Starting Quartz Scheduler now
  48. 2018-07-05 10:50:57 INFO - Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
  49. 2018-07-05 10:50:57 INFO - Started App in 4.164 seconds (JVM running for 4.941)
  50. 2018-07-05 10:50:57 INFO - Job: [SimpleJob: [name=demoJobOne]] launched with the following parameters: [{JobID=1530768057055}]
  51. 2018-07-05 10:50:57 INFO - Job: [FlowJob: [name=demoJobTwo]] launched with the following parameters: [{JobID=1530768057057}]
  52. 2018-07-05 10:50:57 INFO - Executing step: [stepOne]
  53. MyTaskOne start..
  54. MyTaskOne done..
  55. 2018-07-05 10:50:57 INFO - Executing step: [stepTwo]
  56. MyTaskTwo start..
  57. MyTaskTwo done..
  58. 2018-07-05 10:50:57 INFO - Executing step: [stepOne]
  59. 2018-07-05 10:50:57 INFO - Job: [SimpleJob: [name=demoJobOne]] completed with the following parameters: [{JobID=1530768057055}] and the following status: [COMPLETED]
  60. MyTaskOne start..
  61. MyTaskOne done..
  62. 2018-07-05 10:50:57 INFO - Job: [FlowJob: [name=demoJobTwo]] completed with the following parameters: [{JobID=1530768057057}] and the following status: [COMPLETED]
  63. 2018-07-05 10:51:05 INFO - Job: [SimpleJob: [name=demoJobOne]] launched with the following parameters: [{JobID=1530768065955}]
  64. 2018-07-05 10:51:05 INFO - Executing step: [stepOne]
  65. MyTaskOne start..
  66. MyTaskOne done..
  67. 2018-07-05 10:51:05 INFO - Executing step: [stepTwo]
  68. MyTaskTwo start..
  69. MyTaskTwo done..
  70. 2018-07-05 10:51:05 INFO - Job: [SimpleJob: [name=demoJobOne]] completed with the following parameters: [{JobID=1530768065955}] and the following status: [COMPLETED]
  71. 2018-07-05 10:51:15 INFO - Job: [SimpleJob: [name=demoJobOne]] launched with the following parameters: [{JobID=1530768075955}]
  72. 2018-07-05 10:51:15 INFO - Executing step: [stepOne]
  73. MyTaskOne start..
  74. MyTaskOne done..
  75. 2018-07-05 10:51:15 INFO - Executing step: [stepTwo]
  76. MyTaskTwo start..
  77. MyTaskTwo done..
  78. 2018-07-05 10:51:15 INFO - Job: [SimpleJob: [name=demoJobOne]] completed with the following parameters: [{JobID=1530768075955}] and the following status: [COMPLETED]
  79. 2018-07-05 10:51:15 INFO - Job: [FlowJob: [name=demoJobTwo]] launched with the following parameters: [{JobID=1530768075980}]
  80. 2018-07-05 10:51:15 INFO - Executing step: [stepOne]
  81. MyTaskOne start..
  82. MyTaskOne done..
  83. 2018-07-05 10:51:16 INFO - Job: [FlowJob: [name=demoJobTwo]] completed with the following parameters: [{JobID=1530768075980}] and the following status: [COMPLETED]

显然,两个 Spring Batch 作业都基于在 Quartz 触发器中配置的调度执行。

将我的问题放在评论部分。

学习愉快!