目錄:
1. SpringBoot集成Quartz
2. 實現定時任務邏輯
3. 動態創建定時任務
4. Quartz常用類
5. Quartz任務熄火策略
6. Cron表達式速成
7. Quartz使用技巧之通過JobDataMap和SchedulerContext傳遞數據
8. Spring框架Schedule功能的使用方法
Quartz是開源組織OpenSymphony的一個作業調度框架,採用多線程架構,可伸縮性強,可集羣擴展。SpringBoot集成Quartz簡單高效,只需實現Job接口,在方法execute()中添加業務邏輯。
Spring框架自帶了Schedule功能,能滿足小型項目對定時任務的功能需求,同樣支持Cron表達式、固定間隔、固定頻率三種配置方式,但和Quartz相比,不支持任務信息數據庫持久化。
Cron表達式 | 固定間隔 | 固定頻率 | 傳遞數據 | 任務持久化 | |
Quartz | √ | √ | √ | √ | √ |
Spring Schedule | √ | √ | √ | × | × |
本文分享SpringBoot集成和配置Quartz的方法,以及在項目中的實際應用方案,最後介紹Spring 框架自帶的Schedule功能的用法。
代碼文件 | 功能要點 | |
SpringBoot集成Quartz | pom.xml | 引入Quartz依賴:spring-boot-starter-quartz |
application.yml | 配置Quartz屬性,配置Job運行時間cron表達式 | |
QuartzConfig.java | 配置Bean: JobDetail, Trigger,讀取cron運行時間配置 | |
實現定時任務 | QuartzJob.java | 實現Job接口,或者繼承QuartzJobBean類 |
動態創建任務 | CheckController.java | 增加REST接口/chk/job,創建一個Job定時任務,通過Context上下文傳遞數據。 |
項目代碼:https://github.com/jextop/StarterApi/
示例代碼:https://github.com/rickding/HelloJava/tree/master/HelloQuartz
一,SpringBoot集成Quartz
1. 新建SpringBoot項目時,選中Quartz,將自動添加Quartz依賴。
2. 已有SpringBoot項目,可以在pom.xml中直接添加Quartz依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
3. 在application.yml中配置Quartz,指定任務存儲類型:
memory:內存方式,默認。
jdbc:數據庫持久化方式,將創建數據表並保存任務信息。
spring:
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always
job:
quartz:
cron: 0 0/23 * * * ?
4. 在QuartzConfig.java中配置Bean,聲明JobDetail和Trigger,使用cron表達式設置任務運行時間:
@Configuration
@ConfigurationProperties("job.quartz")
public class QuartzConfig {
private String cron;
@Bean
public JobDetail quartzJob() {
return JobBuilder.newJob(QuartzJob.class).storeDurably().build();
}
@Bean
public Trigger quartzTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
return TriggerBuilder.newTrigger()
.forJob(quartzJob())
.withSchedule(scheduleBuilder)
.build();
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
}
二,定時任務QuartzJob.java,繼承QuartzJobBean類,實現Job接口。
從JobExecutionContext中讀取附加信息,執行業務邏輯。
public class QuartzJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
LogUtil.info("quartz job ", new Date());
try {
JextService jextService = (JextService) context.getScheduler().getContext().get("jextService");
if (jextService != null) {
jextService.getInfo(true);
}
} catch (SchedulerException e) {
LogUtil.info(e.getMessage());
}
}
}
三,動態創建定時任務
1. 增加RestController:CheckController.java
2. 增加REST接口/chk/job,創建一個定時任務,添加附加信息到Scheduler上下文中。
@GetMapping(value = "/chk/job")
public Object job() {
JobDetail job = JobBuilder.newJob(QuartzJob.class).build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.forJob(job)
.startAt(new Date())
.build();
Date date = null;
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.getContext().put("jextService", jextService);
date = scheduler.scheduleJob(job, trigger);
scheduler.startDelayed(1);
} catch (SchedulerException e) {
e.printStackTrace();
}
final Date jobDate = date;
return new HashMap<String, Object>() {{
put("chk", "job");
put("date", jobDate);
}};
}
3. Postman調用REST接口創建定時任務示例
四,Quartz常用類
Quartz提供的常用類:Scheduler, SchedulerFactory, Job, JobDetail, JobBuilder, Trigger, TriggerBuilder, ScheduleBuilder,UML類圖如下:
Scheduler時Quartz調度程序的主要接口,維護一個JobDetails和Triggers的註冊表,到觸發時間時,調度程序將執行Job。
調度程序Scheduler實例通過SchedulerFactory工廠創建,有兩個實現類DirectSchedulerFactory和StdSchedulerFactory,前者不寫入持久化數據庫,後者加載quartz.properties屬性配置文件,將查找加載當前目錄和org.quartz包。
任務是實現Job接口的一個類,實現方法execute(),可聲明屬性:
- @DisallowConcurrentExecution,同時只執行一個實例。
- @PersisJobDataAfterExecution,正常執行後將JobDataMap備份。
JobDetail將任務屬性傳遞給Scheduler,通過JobBuilder創建,JobDataMap保存任務實例的狀態信息。
觸發器Trigger通過TriggerBuilder創建,結合ScheduleBuilder設置時間規則,可通過JobDataMap傳遞數據給Job。常用的兩種觸發器:
- SimpleTrigger:指定開始時間、結束時間、重複次數、執行間隔。
- CronTrigger:使用Cron表達式設置時間規則。
五,Quartz任務熄火策略
ScheduleBuilder設置時間規則時,可配置Misfire選項,指定執行失敗熄火時的處理規則:
含義 | 支持ScheduleBuilder | |
IgnoreMisfires | 馬上執行,比如整點9點失敗,系統10:15啓動,會馬上執行9點10點的任務。 | SimpleSchedule CronScheduleBuilder CalendarIntervalScheduleBuilder DailyTimeIntervalScheduleBuilder |
FireNow | 立即再次觸發 | SimpleSchedule |
NowWithExistingCount | 立即再次觸發,不計入總次數。 | SimpleSchedule |
NowWithRemainingCount | 立即再次觸發,計入總次數。 | SimpleSchedule |
NextWithExistingCount | 等待下次執行,不計入總次數。 | SimpleSchedule |
NextWithRemainingCount | 等待下次執行,計入總次數。 | SimpleSchedule |
DoNothing | 不做任何處理,執行下一次週期。 | CronScheduleBuilder CalendarIntervalScheduleBuilder DailyTimeIntervalScheduleBuilder |
FireAndProceed | 合併下一個週期,正常執行。比如整點9點10點失敗,系統10:15啓動,在11點合併執行一次。 | CronScheduleBuilder CalendarIntervalScheduleBuilder DailyTimeIntervalScheduleBuilder |
六,Cron表達式速成
Cron表達式是一個字符串,定義時間規則,由6或7個時間域組成,空格分隔1張表整理清楚含義和規則,並舉例常用表達式,放手邊速查。
時間域序號 | 含義 | 取值範圍 | 特殊字符 |
1 | 秒Seconds | 0-59 | ,-*/ |
2 | 分鐘Minutes | 0-59 | ,-*/ |
3 | 小時Hours | 0-23 | ,-*/ |
4 | 日期DayOfMonth | 1-31 | ,-*/ ? L W C |
5 | 月份Month | 1-12 | ,-*/ JAN-DEC |
6 | 星期DayOfWeek | 1-7 | ,-*/ ? L C # SUN-SAT |
7 | 年Year (可選) | 1970-2099 | ,-*/ |
特殊字符含義
JAN-DEC 月份英語簡稱
SUN-SAT 星期英語簡稱
星期的1表示星期天,2表示星期一,依次類推
* 表示取值範圍內的所有數字
/ 表示每隔固定時間觸發依次,比如0/5表示從0開始每5個單位時間
- 表示兩個數字之間的範圍,比如3-7表示3到7之間,包含3和7
, 表示離散的枚舉數字,比如2,3,5,7表示指定的這幾個時間
? 只能用在日期DayOfMonth和星期DayOfWeek兩個域,表示不指定,避免日期和星期的互相影響,比如指定每月的20日,不管是星期幾,正確寫法是:0 0 0 20 * ?,其中星期只能用?,如果使用*將觸發錯誤。
L 只能用於日期DayOfMonth和星期DayOfWeek,用於日期時表示月份的最後一天,用於星期時不加數字表示週六,加數字表示最後一個周幾,比如0 0 0 ? * 5L表示每月的最後一個星期四
W 只能用於日期DayOfMonth,表示週一到週五有效工作日,將在離指定日期的最近的有效工作日觸發事件。例如在日期使用5W,如果5日是星期六,則將在最近的工作日星期五(4日)觸發。如果5日是星期天,則在6日(星期一)觸發;如果5日在星期一到星期五中的一天,則就在5日觸發。另外一點,W的最近工作日尋找不會跨月份。
LW 兩個字符連用時表示某個月最後一個工作日
# 只能用於星期DayOfWeek,表示每個月第幾個星期幾,比如4#2表示第二個星期三
常用表達式
0/5 * * * * ? 每5秒鐘
0 0/5 * * * ? 每5分鐘
0 0 6 * * ? 每天早上6點
0 0 9,13,19 * * ? 每天上午9點,下午1點,晚上7點
0 0 23-7/2,8 * * ? 每天晚上11點到早上7點之間的每兩個小時,和早上8點
0 0/30 9-17 * * ? 朝九晚五工作時間內每半小時
0 0 9-21 ? * MON-SAT 表示996每天的每小時
0 0 7 LW * ? 每月最後一個工作日早上7點
0 0 4 1 1 ? 每年的1月1日早上4點
七,Quartz使用技巧之通過JobDataMap和SchedulerContext傳遞數據
在Job.execute()方法中實現業務邏輯時,經常需要一些附加信息。Quartz提供了JobExecutionContext上下文傳遞數據。
l 通過JobDataMap傳遞數據
代碼下載:https://github.com/rickding/HelloJava/tree/master/HelloQuartz
src/main/java/com/hello/quartz/
├── QuartzConfig.java
├── QuartzJob.java
代碼文件 | 功能要點 | |
設置數據 | QuartzConfig.java | 創建JobDetail或者Trigger時,調用usingJobData()設置數據 |
讀取數據 | QuartzJob.java | 執行任務時,調用JobExecutionContext.getMergedJobDataMap()獲取數據 |
1,設置數據:JobDetail和Trigger都可以調用usingJobData()方法設置數據。
@Configuration
@ConfigurationProperties("quartz")
public class QuartzConfig {
@Bean
public JobDetail quartzJob() {
JobDataMap dataMap = new JobDataMap() {{
put("job_str", "str_test");
}};
return JobBuilder.newJob(QuartzJob.class)
.usingJobData(dataMap)
.storeDurably()
.build();
}
@Bean
public Trigger quartzTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
JobDataMap dataMap = new JobDataMap() {{
put("trigger_int", 333);
}};
return TriggerBuilder.newTrigger()
.forJob(quartzJob())
.withSchedule(scheduleBuilder)
.usingJobData(dataMap)
.build();
}
}
2,讀取數據:從JobExecutionContext中讀取JobDataMap獲取數據,執行業務邏輯。
public class QuartzJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// get data from context
JobDataMap dataMap = context.getMergedJobDataMap();
for (Map.Entry<String, Object> data : dataMap.entrySet()) {
System.out.printf("%s = %s\n", data.getKey(), data.getValue());
}
// do work
}
}
3,運行輸出:
trigger_int = 333
job_str = str_test
l 通過SchedulerContext傳遞數據
另一個傳遞數據的方式是構建Scheduler時爲SchedulerContext設置數據。代碼詳見上文中第二、第三部分:
代碼下載:https://github.com/jextop/StarterApi/
src/main/java/com/starter/
├── controller/CheckController.java
├── job/QuartzJob.java
讀取數據方式一樣,因爲JobExecutionContext.getMergedJobDataMap()已經將數據合併到一起。也可以通過調用SchedulerContext獲取。
八,Spring框架Schedule功能的使用方法
Spring Schedule支持異步執行定時任務,支持Cron表達式、固定間隔、固定頻率三種配置方式,能滿足小型項目對定時任務的功能需求。示例代碼:https://github.com/rickding/HelloJava/tree/master/HelloQuartz
src/main/java/com/hello/quartz/
├── QuartzApplication.java
├── ScheduledJob.java
代碼文件 | 功能要點 | |
開啓功能 | QuartzApplication.java | SpringApplication增加註解: @EnableScheduling // 打開Schedule功能 @EnableAsysnc // 異步方式執行定時任務 |
配置任務 | ScheduleJob.java | 聲明定時任務,功能註解: @Async // 異步方式執行,聲明在類上面時,作用於類裏面包含的任務 @Scheduled // 聲明定時任務執行時間 |
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class, args);
}
}
1,cron表達式聲明定時任務,比如每10秒執行一次:
@Async
@Scheduled(cron = "0/10 * * * * ?")
public void scheduledCron() {
System.out.printf("scheduled cron: %s\n", new Date());
}
2,固定頻率執行定時任務,比如每20秒執行一次:
@Scheduled(fixedRate = 1000 * 20, initialDelay = 1000 * 20)
public void scheduledRate() {
System.out.printf("fixedRate: %s\n", new Date());
}
3,固定間隔執行定時任務,比如每20秒執行一次:
@Scheduled(fixedDelay = 1000 * 30, initialDelay = 1000 * 30)
public void scheduledDelay() {
System.out.printf("fixedDelay: %s\n", new Date());
}
注意固定頻率和固定間隔兩類定時任務,默認在程序啓動時就會執行一次,可以通過指定initialDelay參數控制首次執行時間。
scheduled cron: Sun Feb 09 18:09:19 CST 2020
fixedRate: Sun Feb 09 18:09:28 CST 2020
scheduled cron: Sun Feb 09 18:09:38 CST 2020
--------------------------------
如果您覺得這篇文章對您有幫助,請點個“贊”,博主感激不盡!
Jext技術社區專注領域:軟件工程實踐,JIRA研發管理,分佈式系統架構,軟件質量保障。