第 4-9 課:Spring Boot 集成 Quartz

在項⽬開發中,經常需要定時任務來幫助我們來做⼀些內容,⽐如定時派息、跑批對賬、業務監控等。
Spring Boot 體系中現在有兩種⽅案可以選擇,第⼀種是 Spring Boot 內置的⽅式簡單註解就可以使⽤,當然
如果需要更復雜的應⽤場景還是得 Quartz 上場,Quartz ⽬前是 Java 體系中最完善的定時⽅案。
⾸先來看看 Spring Boot ⾃帶的定時⽅案。

Spring Boot 內置定時

pom 包配置

pom 包⾥⾯只需要引⼊ Spring Boot Starter 包即可,Spring Boot Starter 包中已經內置了定時的⽅法。

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 </dependency>
</dependencies>

啓動類開啓定時

在啓動類上⾯加上 @EnableScheduling 即可開啓定時:
@Spring BootApplication
@EnableScheduling
public class Application {
 public static void main(String[] args) {
 SpringApplication.run(Application.class, args);
 }
}

創建定時任務實現類

使⽤ Spring Boot ⾃帶的定時⾮常的簡單,只需要在⽅法上⾯添加 @Scheduled 註解即可。
定時任務1
@Component
public class SchedulerTask {
 private int count=0;
 @Scheduled(cron="*/6 * * * * ?")
 private void process(){
 System.out.println("this is scheduler task runing "+(count++));
 }
}
設置 process() 每隔六秒執⾏⼀次,並統計執⾏的次數。
我們還有另外的⼀種⽅案來設置,固定時間週期執⾏⽅法,來看定時任務2
@Component
public class Scheduler2Task {
 private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm
:ss");
 @Scheduled(fixedRate = 6000)
 public void reportCurrentTime() {
 System.out.println("現在時間:" + dateFormat.format(new Date()));
 }
}
啓動項⽬之後,就會在控制檯看到打印的結果。
結果如下:
this is scheduler task runing 0
現在時間:09:44:17
this is scheduler task runing 1
現在時間:09:44:23
this is scheduler task runing 2
現在時間:09:44:29
this is scheduler task runing 3
現在時間:09:44:35
說明兩個⽅法都按照固定 6 秒的頻率來執⾏。
參數說明
@Scheduled 參數可以接受兩種定時的設置,⼀種是我們常⽤的 cron="*/6 * * * * ?",⼀種是 fifixedRate =
6000,兩種都可表示固定週期執⾏定時任務。

fifixedRate 說明

  • @Scheduled(fifixedRate = 6000):上⼀次開始執⾏時間點之後 6 秒再執⾏。
  • @Scheduled(fifixedDelay = 6000):上⼀次執⾏完畢時間點之後 6 秒再執⾏。
  • @Scheduled(initialDelay=1000, fifixedRate=6000):第⼀次延遲 1 秒後執⾏,之後按 fifixedRate 的規則 6 秒執⾏⼀次。

cron 說明

 

cron ⼀共有七位,最後⼀位是年,Spring Boot 定時⽅案中只需要設置六位即可:
  • 第⼀位,表示秒,取值 0 ~ 59
  • 第⼆位,表示分,取值 0 ~ 59
  • 第三位,表示⼩時,取值 0 ~ 23
  • 第四位,⽇期天/⽇,取值 1 ~ 31
  • 第五位,⽇期⽉份,取值 1~12
  • 第六位,星期,取值 1 ~ 7,星期⼀,星期⼆...,注,不是第 1 周、第 2 周的意思,另外,1 表示星期 天,2 表示星期⼀;
  • 第七位,年份,可以留空,取值 1970 ~ 2099
cron 中,還有⼀些特殊的符號,含義如下:
  • *)星號,可以理解爲每的意思,每秒、每分、每天、每⽉、每年...
  • ?)問號,問號只能出現在⽇期和星期這兩個位置,表示這個位置的值不確定,每天 3 點執⾏,因此 第六位星期的位置,是不需要關注的,就是不確定的值;同時,⽇期和星期是兩個相互排斥的元素,通 過問號來表明不指定值,⽐如 1 10 ⽇是星期⼀,如果在星期的位置另指定星期⼆,就前後衝突⽭盾 了。
  • -)減號,表達⼀個範圍,如在⼩時字段中使⽤“10 ~ 12”,則表示從 10 12 點,即 101112
  • ,)逗號,表達⼀個列表值,如在星期字段中使⽤“124”,則表示星期⼀、星期⼆、星期四。
  • /)斜槓,如 x/yx 是開始值,y 是步⻓,⽐如在第⼀位(秒),0/15 就是從 0 秒開始,每隔 15 秒執 ⾏⼀次,最後就是 015304560,另 */y,等同於 0/y
下⾯列舉⼏個常⽤的例⼦。
  • 0 0 3 * * ? :每天 3 點執⾏;
  • 0 5 3 * * ?:每天 3 5 分執⾏;
  • 0 5 3 ? * *:每天 3 5 分執⾏,與上⾯作⽤相同;
  • 0 5/10 3 * * ?:每天 3 點的 5 分、15 分、25 分、35 分、45 分、55 分這⼏個時間點執⾏;
  • 0 10 3 ? * 1:每週星期天,3 10 分執⾏,注,1 表示星期天;
  • 0 10 3 ? * 1#3:每個⽉的第三個星期,星期天執⾏,# 號只能出現在星期的位置。
以上就是 Spring Boot ⾃定的定時⽅案,使⽤起來⾮常的簡單⽅便。
 

Quartz

Quartz 介紹

Quartz OpenSymphony 開源組織在 Job Scheduling 領域⼜⼀個開源項⽬,是完全由 Java 開發的⼀個開
源任務⽇程管理系統,任務進度管理器就是⼀個在預先確定(被納⼊⽇程)的時間到達時,負責執⾏(或
者通知)其他軟件組件的系統。 Quartz 是⼀個開源的作業調度框架,它完全由 Java 寫成,並設計⽤於
J2SE J2EE 應⽤中,它提供了巨⼤的靈活性⽽不犧牲簡單性。
當定時任務愈加複雜時,使⽤ Spring 註解 @Schedule 已經不能滿⾜業務需要。

Quartz 的優點

  • 豐富的 Job 操作 API
  • ⽀持多種配置;
  • Spring Boot ⽆縫集成;
  • ⽀持持久化;
  • ⽀持集羣;
  • Quartz 還⽀持開源,是⼀個功能豐富的開源作業調度庫,可以集成到⼏乎任何 Java 應⽤程序中。

Quartz 體系結構

 

明⽩ Quartz 怎麼⽤,⾸先要了解 Job(任務)、JobDetail(任務信息)、Trigger(觸發器)和
Scheduler(調度器)這 4 個核⼼的概念。
 
1Job:是⼀個接⼝,只定義⼀個⽅法 executeJobExecutionContext context),在實現接⼝的
execute ⽅法中編寫所需要定時執⾏的 Job(任務),JobExecutionContext 類提供了調度應⽤的⼀些信息;
Job 運⾏時的信息保存在 JobDataMap 實例中。
 
2JobDetailQuartz 每次調度 Job 時,都重新創建⼀個 Job 實例,因此它不接受⼀個 Job 的實例,相反
它接收⼀個 Job 實現類(JobDetail,描述 Job 的實現類及其他相關的靜態信息,如 Job 名字、描述、關聯
監聽器等信息),以便運⾏時通過 newInstance() 的反射機制實例化 Job
 
3Trigger:是⼀個類,描述觸發 Job 執⾏的時間觸發規則,主要有 SimpleTrigger CronTrigger 這兩個
⼦類。當且僅當需調度⼀次或者以固定時間間隔週期執⾏調度,SimpleTrigger 是最適合的選擇;⽽
CronTrigger 則可以通過 Cron 表達式定義出各種複雜時間規則的調度⽅案:如⼯作⽇周⼀到週五的 1500
~ 1600 執⾏調度等。
 
4Scheduler:調度器就相當於⼀個容器,裝載着任務和觸發器,該類是⼀個接⼝,代表⼀個 Quartz 的獨
⽴運⾏容器,Trigger JobDetail 可以註冊到 Scheduler 中,兩者在 Scheduler 中擁有各⾃的組及名稱,組
及名稱是 Scheduler 查找定位容器中某⼀對象的依據,Trigger 的組及名稱必須唯⼀,JobDetail 的組和名稱
也必須唯⼀(但可以和 Trigger 的組和名稱相同,因爲它們是不同類型的)。Scheduler 定義了多個接⼝⽅
法,允許外部通過組及名稱訪問和控制容器中 Trigger JobDetail
 
四者其關係如下圖所示:
Job 爲作業的接⼝,爲任務調度的對象;JobDetail ⽤來描述 Job 的實現類及其他相關的靜態信息;Trigger
做爲作業的定時管理⼯具,⼀個 Trigger 只能對應⼀個作業實例,⽽⼀個作業實例可對應多個觸發器;
Scheduler 做爲定時任務容器,是 Quartz 最上層的東⻄,它提攜了所有觸發器和作業,使它們協調⼯作,每
Scheduler 都存有 JobDetail Trigger 的註冊,⼀個 Scheduler 中可以註冊多個 JobDetail 和多個
Trigger

Spring Boot Quartz

Spring Boot 2.0 提供了 spring-boot-starter-quartz 組件集成 Quartz,讓我們在項⽬中使⽤ Quartz 變得簡 單。

配置內容

配置 pom.xml

添加 spring-boot-starter-quartz 組件:
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

簡單示例

配置完成之後先來做⼀個最簡單的示例,使⽤ Quartz 定時輸出 Hello World
⾸先定義⼀個 Job 需要繼承 QuartzJobBean,示例中 Job 定義⼀個變量 Name,⽤於在定時執⾏的時候傳
⼊。
public class SampleJob extends QuartzJobBean {
 private String name;
 public void setName(String name) {
 this.name = name;
 }
 @Override
 protected void executeInternal(JobExecutionContext context)
 throws JobExecutionException {
 System.out.println(String.format("Hello %s!", this.name));
 }
}
接下來構建 JobDetail,並且構建時傳⼊ name 屬性的值,構建 JobTrigger scheduleBuilder,最後使⽤
Scheduler 啓動定時任務。
@Configuration
public class SampleScheduler {
 @Bean
 public JobDetail sampleJobDetail() {
 return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob")
 .usingJobData("name", "World").storeDurably().build();
 }
 @Bean
 public Trigger sampleJobTrigger() {
 SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedu
le()
 .withIntervalInSeconds(2).repeatForever();
 return TriggerBuilder.newTrigger().forJob(sampleJobDetail())
 .withIdentity("sampleTrigger").withSchedule(scheduleBuilder).build
();
 }
}
  • JobBuilder ⽆構造函數,只能通過 JobBuilder 的靜態⽅法 newJob(Class<? extends Job> jobClass) JobBuilder 實例。
  • withIdentity ⽅法可以傳⼊兩個參數 withIdentity(String name,String group) 來定義 TriggerKey,也可以 不設置,像上⽂示例中會⾃動⽣成⼀個獨⼀⽆⼆的 TriggerKey ⽤來區分不同的 Trigger
啓動項⽬後每隔兩秒輸出:Hello World!
Hello World!
Hello World!
Hello World!
...

CronSchedule 示例

CronSchedule 可以設置更靈活的使⽤⽅式,定時設置可以參考上⾯的 cron 表達式。
 
⾸先定義兩個 Job
public class ScheduledJob implements Job {
 @Override 
 public void execute(JobExecutionContext context) throws JobExecutionException 
{
 System.out.println("schedule job1 is running ...");
 } 
}
ScheduledJob2 ScheduledJob 代碼基本⼀致。
按照使⽤ Quartz 的邏輯,構建 jobDetailCronTrigger,最後使⽤ scheduler 關聯 jobDetail
CronTriggerscheduleJob1 設置每間隔 6 秒執⾏⼀次。
private void scheduleJob1(Scheduler scheduler) throws SchedulerException{
 JobDetail jobDetail = JobBuilder.newJob(ScheduledJob.class) .withIdentity("job
1", "group1").build();
 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/6 * 
* * * ?");
 CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1",
 "group1") .withSchedule(scheduleBuilder).build();
 scheduler.scheduleJob(jobDetail,cronTrigger); 
}
  • CronScheduleBuilder.cronSchedule("0/6 * * * * ?"),按照 cron 表達式設置定時任務的執⾏週期。
ScheduleJob2 的內容和 ScheduleJob1 基本⼀致,時間設置爲間隔 12 秒執⾏⼀次。
使⽤ Scheduler 啓動兩個定時任務。
public void scheduleJobs() throws SchedulerException {
 Scheduler scheduler = schedulerFactoryBean.getScheduler();
 scheduleJob1(scheduler);
 scheduleJob2(scheduler); 
}

何時觸發定時任務

我們有兩種⽅案來觸發 CronSchedule 定時任務,⼀種是啓動時調⽤ scheduleJobs() 來啓動定時任務,另外
⼀種⽅案使⽤ Spring Boot ⾃帶的 Scheduled 在特定時間觸發啓動。
 
第⼀種⽅案,啓動時觸發定時任務:
@Component
public class MyStartupRunner implements CommandLineRunner {
 @Autowired
 public CronSchedulerJob scheduleJobs;
 @Override
 public void run(String... args) throws Exception {
 scheduleJobs.scheduleJobs();
 System.out.println(">>>>>>>>>>>>>>>定時任務開始執⾏<<<<<<<<<<<<<");
 }
}
定時⼀個 Runner,繼承 CommandLineRunner 並重新 run ⽅法,在 run ⽅法中調⽤ scheduleJobs() 來啓動
定時任務。
 
第⼆種⽅案,特定時間啓動定時任務:
@Configuration
@EnableScheduling
@Component
public class SchedulerListener { 
 @Autowired
 public CronSchedulerJob scheduleJobs;
 @Scheduled(cron="0 30 11 25 11 ?")
 public void schedule() throws SchedulerException {
 scheduleJobs.scheduleJobs();
 } 
}
啓動項⽬後每隔 6 秒輸出 job1 內容,每隔 12 秒輸出 job2 內容,再加上上⾯示例每兩秒輸出的 Hello
World,輸出內容如下:GitChat
Hello World!
Hello World!
Hello World!
schedule job1 is running ...
Hello World!
Hello World!
Hello World!
schedule job1 is running ...
schedule job2 is running ...
...
⼀般情況下,建議使⽤第⼀種⽅案來啓動定時任務;第⼆種⽅案設置固定⽇期時,需要考慮重複啓動定時任
務的情況,重複啓動定時任務會報錯。
注意,兩種啓動⽅案,在項⽬中選擇⼀種使⽤即可,否則會導致重複啓動定時任務⽽報錯。

總結

通過上⾯的示例可以看出,如果僅需要執⾏簡單定時任務,就可以使⽤ Spring Boot ⾃帶 Scheduled,⾮常
簡單、⽅便;但如果需要在項⽬中執⾏⼤量的批任務處理時,可以採⽤ Quartz 來解決,Spring Boot 2.0
提供了對 Quartz 的⽀持,讓我們在項⽬使⽤的過程中更加的靈活簡潔。
 
 
 
 
發佈了89 篇原創文章 · 獲贊 19 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章