目錄
這幾天接觸到了Quartz,簡單的做下筆記吧,好記性不如爛筆頭。本文主要圍繞Quartz的簡單使用以及基本原理理解大多知識是從別的博客趴過來的,文章的最後會附上原文鏈接^_^。
Quartz-JOB-Framework 中文版和QUartz開發指南:https://download.csdn.net/download/sinat_37064286/11423292
一、介紹
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,是完全由java開發的一個開源的任務日程管理系統,“任務進度管理器”就是一個在預先確定(被納入日程)的時間到達時,負責執行(或者通知)其他軟件組件的系統。
Quartz用一個小Java庫發佈文件(.jar文件),這個庫文件包含了所有Quartz核心功能。這些功能的主要接口(API)是Scheduler接口。它提供了簡單的操作,例如:將任務納入日程或者從日程中取消,開始/停止/暫停日程進度。
二、背景知識
同類產品:
- Java自帶的java.util.Timer類,這個類允許你調度一個java.util.TimerTask任務。使用這種方式可以讓你的程序按照某一個頻度執行,但不能在指定時間運行。一般用的較少,這篇文章將不做詳細介紹。
- 使用Quartz,這是一個功能比較強大的的調度器,可以讓你的程序在指定時間執行,也可以按照某一個頻度執行,配置起來稍顯複雜,稍後會詳細介紹。
- Spring3.0以後自帶的task,可以將它看成一個輕量級的Quartz,而且使用起來比Quartz簡單許多,稍後會介紹。
三、Quartz可以用來做什麼?
Quartz是一個任務調度框架。比如你遇到這樣的問題
- 想每月25號,信用卡自動還款
- 想每年4月1日自己給當年暗戀女神發一封匿名賀卡
- 想每隔1小時,備份一下自己的愛情動作片 學習筆記到雲盤
這些問題總結起來就是:在某一個有規律的時間點幹某件事。並且時間的觸發的條件可以非常複雜(比如每月最後一個工作日的17:50),複雜到需要一個專門的框架來幹這個事。 Quartz就是來幹這樣的事,你給它一個觸發條件的定義,它負責到了時間點,觸發相應的Job起來幹活。
五、核心概念
Quartz的原理不是很複雜,只要搞明白幾個概念,然後知道如何去啓動和關閉一個調度程序即可。
1、Job
表示一個工作,要執行的具體內容。此接口中只有一個方法 :void execute(JobExecutionContext context)。主要有兩種類型的 job:無狀態的(stateless)和有狀態的(stateful)。對於同一個 trigger 來說,有狀態的 job 不能被並行執行,只有上一次觸發的任務被執行完之後,才能觸發下一次執行。Job 主要有兩種屬性:volatility 和 durability,其中 volatility 表示任務是否被持久化到數據庫存儲,而 durability 表示在沒有 trigger 關聯的時候任務是否被保留。兩者都是在值爲 true 的時候任務被持久化或保留。一個 job 可以被多個 trigger 關聯,但是一個 trigger 只能關聯一個 job。
Job併發
Job是有可能併發執行的,比如一個任務要執行10秒中,而調度算法是每秒中觸發1次,那麼就有可能多個任務被併發執行。
有時候我們並不想任務併發執行,比如這個任務要去”獲得數據庫中所有未發送郵件的名單“,如果是併發執行,就需要一個數據庫鎖去避免一個數據被多次處理。這個時候一個@DisallowConcurrentExecution解決這個問題。
就是這樣
public class DoNothingJob implements Job { @DisallowConcurrentExecution public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("do nothing"); } }
注意,@DisallowConcurrentExecution是對JobDetail實例生效,也就是如果你定義兩個JobDetail,引用同一個Job類,是可以併發執行的。
JobExecutionException
Job.execute()方法是不允許拋出除JobExecutionException之外的所有異常的(包括RuntimeException),所以編碼的時候,最好是try-catch住所有的Throwable,小心處理。
2、JobDetail
JobDetail表示一個具體的可執行的調度程序,Job是這個可執行程調度程序所要執行的內容,另外JobDetail還包含了這個任務調度的方案和策略。
JobDetail & Job區別
JobDetail 定義的是任務數據,而真正的執行邏輯是在Job中,例子中是HelloQuartz。 爲什麼設計成JobDetail + Job,不直接使用Job?這是因爲任務是有可能併發執行,如果Scheduler直接使用Job,就會存在對同一個Job實例併發訪問的問題。而JobDetail & Job 方式,sheduler每次執行,都會根據JobDetail創建一個新的Job實例,這樣就可以規避併發訪問的問題。
JobDataMap
在Quartz中,每次Scheduler執行Job時,在調用其execute()方法之前,它需要先根據JobDetail提供的Job類型創建一個Job class的實例,在任務執行完以後,Job class的實例會被丟棄,Jvm的垃圾回收器會將它們回收。因此編寫Job的具體實現時,需要注意:
(1) 它必須具有一個無參數的構造函數;
(2) 它不應該有靜態數據類型,因爲每次Job執行完以後便被回收,因此在多次執行時靜態數據沒法被維護。
Job每次都是newInstance的實例,那我怎麼傳值給它? 比如我現在有兩個發送郵件的任務,一個是發給"liLei",一個發給"hanmeimei",不能說我要寫兩個Job實現類LiLeiSendEmailJob和HanMeiMeiSendEmailJob。實現的辦法是通過JobDataMap。
每一個JobDetail都會有一個JobDataMap。JobDataMap本質就是一個Map的擴展類,只是提供了一些更便捷的方法,比如getString()之類的。
我們可以在定義JobDetail,加入屬性值,方式有二:
newJob().usingJobData("age", 18) //加入屬性到ageJobDataMap or job.getJobDataMap().put("name", "quertz"); //加入屬性name到JobDataMap
然後在Job中可以獲取這個JobDataMap的值,方式同樣有二:
public class HelloQuartz implements Job { private String name; public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); JobDataMap map = detail.getJobDataMap(); //方法一:獲得JobDataMap System.out.println("say hello to " + name + "[" + map.getInt("age") + "]" + " at " + new Date()); } //方法二:屬性的setter方法,會將JobDataMap的屬性自動注入 public void setName(String name) { this.name = name; } }
對於同一個JobDetail實例,執行的多個Job實例,是共享同樣的JobDataMap,也就是說,如果你在任務裏修改了裏面的值,會對其他Job實例(併發的或者後續的)造成影響。
除了JobDetail,Trigger同樣有一個JobDataMap,共享範圍是所有使用這個Trigger的Job實例。
3、Trigger代表一個調度參數的配置,什麼時候去調,用於定義任務調度時間規則。Quartz 中主要提供了四種類型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。這四種 trigger 可以滿足企業應用中的絕大部分需求。
數據存儲
Quartz 中的 trigger 和 job 需要存儲下來才能被使用。Quartz 中有兩種存儲方式:RAMJobStore, JobStoreSupport。
RAMJobStore 不要外部數據庫,配置容易,運行速度快 因爲調度程序信息是存儲在被分配給JVM的內存裏面,所以,當應用程序停止運行時,所有調度信息將被丟失。另外因爲存儲到JVM內存裏面,所以可以存儲多少個Job和Trigger將會受到限制
JDBCJobStore 支持集羣,因爲所有的任務信息都會保存到數據庫中,可以控制事物,還有就是如果應用服務器關閉或者重啓,任務信息都不會丟失,並且可以恢復因服務器關閉或者重啓而導致執行失敗的任務 運行速度的快慢取決與連接數據庫的快慢在 Quartz 中,JobStoreSupport 使用一個驅動代理來操作 trigger 和 job 的數據存儲:StdJDBCDelegate。StdJDBCDelegate 實現了大部分基於標準 JDBC 的功能接口,但是對於各種數據庫來說,需要根據其具體實現的特點做某些特殊處理,因此各種數據庫需要擴展 StdJDBCDelegate 以實現這些特殊處理。Quartz 已經自帶了一些數據庫的擴展實現,可以直接使用。
種類
SimpleTrigger
指定從某一個時間開始,以一定的時間間隔(單位是毫秒)執行的任務。
它適合的任務類似於:9:00 開始,每隔1小時,執行一次。
它的屬性有:
- repeatInterval 重複間隔
- repeatCount 重複次數。實際執行次數是 repeatCount+1。因爲在startTime的時候一定會執行一次。** 下面有關repeatCount 屬性的都是同理。 **
例子:
simpleSchedule() .withIntervalInHours(1) //每小時執行一次 .repeatForever() //次數不限 .build(); simpleSchedule() .withIntervalInMinutes(1) //每分鐘執行一次 .withRepeatCount(10) //次數爲10次 .build();
CalendarIntervalTrigger
類似於SimpleTrigger,指定從某一個時間開始,以一定的時間間隔執行的任務。 但是不同的是SimpleTrigger指定的時間間隔爲毫秒,沒辦法指定每隔一個月執行一次(每月的時間間隔不是固定值),而CalendarIntervalTrigger支持的間隔單位有秒,分鐘,小時,天,月,年,星期。
相較於SimpleTrigger有兩個優勢:1、更方便,比如每隔1小時執行,你不用自己去計算1小時等於多少毫秒。 2、支持不是固定長度的間隔,比如間隔爲月和年。但劣勢是精度只能到秒。
它適合的任務類似於:9:00 開始執行,並且以後每週 9:00 執行一次
它的屬性有:
- interval 執行間隔
- intervalUnit 執行間隔的單位(秒,分鐘,小時,天,月,年,星期)
例子:
calendarIntervalSchedule() .withIntervalInDays(1) //每天執行一次 .build(); calendarIntervalSchedule() .withIntervalInWeeks(1) //每週執行一次 .build();
DailyTimeIntervalTrigger
指定每天的某個時間段內,以一定的時間間隔執行任務。並且它可以支持指定星期。
它適合的任務類似於:指定每天9:00 至 18:00 ,每隔70秒執行一次,並且只要週一至週五執行。
它的屬性有:
- startTimeOfDay 每天開始時間
- endTimeOfDay 每天結束時間
- daysOfWeek 需要執行的星期
- interval 執行間隔
- intervalUnit 執行間隔的單位(秒,分鐘,小時,天,月,年,星期)
- repeatCount 重複次數
例子:
dailyTimeIntervalSchedule() .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //第天9:00開始 .endingDailyAt(TimeOfDay.hourAndMinuteOfDay(16, 0)) //16:00 結束 .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //週一至週五執行 .withIntervalInHours(1) //每間隔1小時執行一次 .withRepeatCount(100) //最多重複100次(實際執行100+1次) .build(); dailyTimeIntervalSchedule() .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //第天9:00開始 .endingDailyAfterCount(10) //每天執行10次,這個方法實際上根據 startTimeOfDay+interval*count 算出 endTimeOfDay .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //週一至週五執行 .withIntervalInHours(1) //每間隔1小時執行一次 .build();
CronTrigger
適合於更復雜的任務,它支持類型於Linux Cron的語法(並且更強大)。基本上它覆蓋了以上三個Trigger的絕大部分能力(但不是全部)—— 當然,也更難理解。
它適合的任務類似於:每天0:00,9:00,18:00各執行一次。
它的屬性只有:
- Cron表達式。但這個表示式本身就夠複雜了。下面會有說明。
例子:
cronSchedule("0 0/2 8-17 * * ?") // 每天8:00-17:00,每隔2分鐘執行一次 .build(); cronSchedule("0 30 9 ? * MON") // 每週一,9:30執行一次 .build(); weeklyOnDayAndHourAndMinute(MONDAY,9, 30) //等同於 0 30 9 ? * MON .build();
關於name和group
JobDetail和Trigger都有name和group。
name是它們在這個sheduler裏面的唯一標識。如果我們要更新一個JobDetail定義,只需要設置一個name相同的JobDetail實例即可。
group是一個組織單元,sheduler會提供一些對整組操作的API,比如 scheduler.resumeJobs()。
StartTime & EndTime
startTime和endTime指定的Trigger會被觸發的時間區間。在這個區間之外,Trigger是不會被觸發的。
** 所有Trigger都會包含這兩個屬性 **
優先級(Priority)
當scheduler比較繁忙的時候,可能在同一個時刻,有多個Trigger被觸發了,但資源不足(比如線程池不足)。那麼這個時候比剪刀石頭布更好的方式,就是設置優先級。優先級高的先執行。
需要注意的是,優先級只有在同一時刻執行的Trigger之間纔會起作用,如果一個Trigger是9:00,另一個Trigger是9:30。那麼無論後一個優先級多高,前一個都是先執行。
優先級的值默認是5,當爲負數時使用默認值。最大值似乎沒有指定,但建議遵循Java的標準,使用1-10,不然鬼才知道看到【優先級爲10】是時,上頭還有沒有更大的值。
Misfire(錯失觸發)策略
類似的Scheduler資源不足的時候,或者機器崩潰重啓等,有可能某一些Trigger在應該觸發的時間點沒有被觸發,也就是Miss Fire了。這個時候Trigger需要一個策略來處理這種情況。每種Trigger可選的策略各不相同。
這裏有兩個點需要重點注意:
- MisFire的觸發是有一個閥值,這個閥值是配置在JobStore的。比RAMJobStore是org.quartz.jobStore.misfireThreshold。只有超過這個閥值,纔會算MisFire。小於這個閥值,Quartz是會全部重新觸發。
所有MisFire的策略實際上都是解答兩個問題:
- 已經MisFire的任務還要重新觸發嗎?
- 如果發生MisFire,要調整現有的調度時間嗎?
比如SimpleTrigger的MisFire策略有:
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
這個不是忽略已經錯失的觸發的意思,而是說忽略MisFire策略。它會在資源合適的時候,重新觸發所有的MisFire任務,並且不會影響現有的調度時間。
比如,SimpleTrigger每15秒執行一次,而中間有5分鐘時間它都MisFire了,一共錯失了20個,5分鐘後,假設資源充足了,並且任務允許併發,它會被一次性觸發。
這個屬性是所有Trigger都適用。
MISFIRE_INSTRUCTION_FIRE_NOW
忽略已經MisFire的任務,並且立即執行調度。這通常只適用於只執行一次的任務。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
將startTime設置當前時間,立即重新調度任務,包括的MisFire的
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
類似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,區別在於會忽略已經MisFire的任務
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
在下一次調度時間點,重新開始調度任務,包括的MisFire的
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
類似於MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,區別在於會忽略已經MisFire的任務。
MISFIRE_INSTRUCTION_SMART_POLICY
所有的Trigger的MisFire默認值都是這個,大致意思是“把處理邏輯交給聰明的Quartz去決定”。基本策略是,
- 如果是隻執行一次的調度,使用MISFIRE_INSTRUCTION_FIRE_NOW
- 如果是無限次的調度(repeatCount是無限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
- 否則,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
MisFire的東西挺繁雜的,可以參考這篇
4、Scheduler代表一個調度容器,一個調度容器中可以註冊多個JobDetail和Trigger。當Trigger與JobDetail組合,就可以被Scheduler容器調度了。 scheduler 由 scheduler 工廠創建:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二種工廠 StdSchedulerFactory 使用較多,因爲 DirectSchedulerFactory 使用起來不夠方便,需要作許多詳細的手工編碼設置。 Scheduler 主要有三種:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。本文以最常用的 StdScheduler 爲例講解。
5、misfire:錯過的,指本來應該被執行但實際沒有被執行的任務調度
Quartz 核心元素關係圖
六、使用流程
1、創建調度工廠(); //工廠模式
2、根據工廠取得調度器實例(); //工廠模式
3、Builder模式構建子組件<Job,Trigger> // builder模式, 如JobBuilder、TriggerBuilder、DateBuilder
4、通過調度器組裝子組件 調度器.組裝<子組件1,子組件2...> //工廠模式
5、調度器.start(); //工廠模式
七、一個最簡單入門實例
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
/**
* quartz定時器測試
*
* @author leizhimin 2009-7-23 8:49:01
*/
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(new Date() + ": doing something...");
System.out.println("現在的時間是:"+ sf.format(date));
//具體的業務邏輯
System.out.println("開始生成任務報表 或 開始發送郵件....");
JobKey key = jobExecutionContext.getJobDetail().getKey();
System.out.println("jobDetail 的name : "+key.getName()); //打印jobDetail 的name
System.out.println("jobDetail 的group : "+key.getGroup()); //打印jobDetail 的group
JobDataMap jobDetailDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String message = jobDetailDataMap.getString("message"); //
float floatJobValue = jobDetailDataMap.getFloat("FloatJobValue");
System.out.println("jobDataMap定義的message的值 : "+message ); //打印jobDataMap定義的message的值
System.out.println("jobDataMap定義的floatJobValue的值 : "+floatJobValue ); //jobDataMap定義的floatJobValue的值
}
}
class Test {
public static void main(String[] args) {
//1、創建JobDetial對象
JobDetail jobDetail = new JobDetail();
//設置工作項
jobDetail.setJobClass(MyJob.class);
jobDetail.setName("MyJob_1");
jobDetail.setGroup("JobGroup_1");
//2、創建Trigger對象
SimpleTrigger strigger = new SimpleTrigger();
strigger.setName("Trigger_1");
strigger.setGroup("Trigger_Group_1");
strigger.setStartTime(new Date());
//設置重複停止時間,並銷燬該Trigger對象
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis() + 1000 * 1L);
strigger.setEndTime(c.getTime());
strigger.setFireInstanceId("Trigger_1_id_001");
//設置重複間隔時間
strigger.setRepeatInterval(1000 * 1L);
//設置重複執行次數
strigger.setRepeatCount(3);
//3、創建Scheduler對象,並配置JobDetail和Trigger對象
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = null;
try {
scheduler = sf.getScheduler();
scheduler.scheduleJob(jobDetail, strigger);
//4、並執行啓動、關閉等操作
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
// try {
// //關閉調度器
// scheduler.shutdown(true);
// } catch (SchedulerException e) {
// e.printStackTrace();
// }
}
}
執行結果:
當把結束時間改爲:
//設置重複停止時間,並銷燬該Trigger對象
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis() + 1000 * 1L);
strigger.setEndTime(c.getTime());
執行結果:
當添加一條關閉調度器的語句:
//4、並執行啓動、關閉等操作
scheduler.start();
scheduler.shutdown(true);
程序執行結果:
Thu Jul 23 10:11:50 CST 2009: doing something...
Process finished with exit code 0
僅僅執行了一次,這一次能執行完,原因是設定了scheduler.shutdown(true);true表示等待本次任務執行完成後停止。
從這裏也可以看出,scheduler是個容器,scheduler控制jobDetail的執行,控制的策略是通過trigger。
當scheduler容器啓動後,jobDetail才能根據關聯的trigger策略去執行。當scheduler容器關閉後,所有的jobDetail都停止執行。
八、表關係和解釋
表關係
qrtz_blob_triggers Trigger作爲Blob類型存儲(用於Quartz用戶用JDBC創建他們自己定製的Trigger類型,JobStore 並不知道如何存儲實例的時候)
qrtz_calendars 以Blob類型存儲Quartz的Calendar日曆信息, quartz可配置一個日曆來指定一個時間範圍
qrtz_cron_triggers 存儲Cron Trigger,包括Cron表達式和時區信息。
qrtz_fired_triggers 存儲與已觸發的Trigger相關的狀態信息,以及相聯Job的執行信息
qrtz_job_details 存儲每一個已配置的Job的詳細信息
qrtz_locks 存儲程序的非觀鎖的信息(假如使用了悲觀鎖)
qrtz_paused_trigger_graps 存儲已暫停的Trigger組的信息
qrtz_scheduler_state 存儲少量的有關 Scheduler的狀態信息,和別的 Scheduler 實例(假如是用於一個集羣中)
qrtz_simple_triggers 存儲簡單的 Trigger,包括重複次數,間隔,以及已觸的次數
qrtz_triggers 存儲已配置的 Trigger的信息
qrzt_simprop_triggers
九、cron表達式編寫規則
1. Quartz Cron 表達式支持7個域 ,分別是秒/分/時/日/月/周/年.期中年是非必須項.如下圖
名稱 | 是否必須 | 允許值 | 特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | , - * / |
時 | 是 | 0-23 | , - * / |
日 | 是 | 1-31 | , - * ? / L W C |
月 | 是 | 1-12 或 JAN-DEC | , - * / |
周 | 是 | 1-7 或 SUN-SAT | , - * ? / L C # |
年 | 否 | 空 或 1970-2099 | , - * / |
注意在cron表達式中不區分大小寫.
星號(*):可用在所有字段中,表示對應時間域的每一個時刻,例如, 在分鐘字段時,表示“每分鐘”;
問號(?):該字符只在日期和星期字段中使用,它通常指定爲“無意義的值”,相當於點位符;
減號(-):表達一個範圍,如在小時字段中使用“10-12”,則表示從10到12點,即10,11,12;
逗號(,):表達一個列表值,如在星期字段中使用“MON,WED,FRI”,則表示星期一,星期三和星期五;
斜槓(/):x/y表達一個等步長序列,x爲起始值,y爲增量步長值。如在分鐘字段中使用0/15,則表示爲0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可以使用*/y,它等同於0/y;
L:該字符只在日期和星期字段中使用,代表“Last”的意思,但它在兩個字段中意思不同。L在日期字段中,表示這個月份的最後一天,如一月的31號,非閏年二月的28號;如果L用在星期中,則表示星期六,等同於7。但是,如果L出現在星期字段裏,而且在前面有一個數值X,則表示“這個月的最後X天”,例如,6L表示該月的最後星期五;
W:該字符只能出現在日期字段裏,是對前導日期的修飾,表示離該日期最近的工作日。例如15W表示離該月15號最近的工作日,如果該月15號是星期六,則匹配14號星期五;如果15日是星期日,則匹配16號星期一;如果15號是星期二,那結果就是15號星期二。但必須注意關聯的匹配日期不能夠跨月,如你指定1W,如果1號是星期六,結果匹配的是3號星期一,而非上個月最後的那天。W字符串只能指定單一日期,而不能指定日期範圍;
LW組合:在日期字段可以組合使用LW,它的意思是當月的最後一個工作日;
井號(#):該字符只能在星期字段中使用,表示當月某個工作日。如6#3表示當月的第三個星期五(6表示星期五,#3表示當前的第三個),而4#5表示當月的第五個星期三,假設當月沒有第五個星期三,忽略不觸發;
C:該字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是計劃所關聯的日期,如果日期沒有被關聯,則相當於日曆中所有日期。例如5C在日期字段中就相當於日曆5日以後的第一天。1C在星期字段中相當於星期日後的第一天。
Cron表達式對特殊字符的大小寫不敏感,對代表星期的縮寫英文大小寫也不敏感。
2.官方的一些案例
表示式 | 說明 |
---|---|
0 0 12 * * ? | 每天12點運行 |
0 15 10 ? * * | 每天10:15運行 |
0 15 10 * * ? | 每天10:15運行 |
0 15 10 * * ? * | 每天10:15運行 |
0 15 10 * * ? 2008 | 在2008年的每天10:15運行 |
0 * 14 * * ? | 每天14點到15點之間每分鐘運行一次,開始於14:00,結束於14:59。 |
0 0/5 14 * * ? | 每天14點到15點每5分鐘運行一次,開始於14:00,結束於14:55。 |
0 0/5 14,18 * * ? | 每天14點到15點每5分鐘運行一次,此外每天18點到19點每5鍾也運行一次。 |
0 0-5 14 * * ? | 每天14:00點到14:05,每分鐘運行一次。 |
0 10,44 14 ? 3 WED | 3月每週三的14:10分到14:44,每分鐘運行一次。 |
0 15 10 ? * MON-FRI | 每週一,二,三,四,五的10:15分運行。 |
0 15 10 15 * ? | 每月15日10:15分運行。 |
0 15 10 L * ? | 每月最後一天10:15分運行。 |
0 15 10 ? * 6L | 每月最後一個星期五10:15分運行。 |
0 15 10 ? * 6L 2007-2009 | 在2007,2008,2009年每個月的最後一個星期五的10:15分運行。 |
0 15 10 ? * 6#3 | 每月第三個星期五的10:15分運行。 |
以上就可以實現大部分的業務的需求了,附上cron表達式在線生成器:https://www.pppet.net/
十、總結
1、搞清楚了上Quartz容器執行作業的的原理和過程,以及作業形成的方式,作業註冊到容器的方法。就認識明白了Quartz的核心原理。
2、Quartz雖然很龐大,但是一切都圍繞這個核心轉,爲了配置強大時間調度策略,可以研究專門的CronTrigger。要想靈活配置作業和容器屬性,可以通過Quartz的properties文件或者XML來實現。
3、要想調度更多的持久化、結構化作業,可以通過數據庫讀取作業,然後放到容器中執行。
4、所有的一切都圍繞這個核心原理轉,搞明白這個了,再去研究更高級用法就容易多了。
5、Quartz與Spring的整合也非常簡單,Spring提供一組Bean來支持:MethodInvokingJobDetailFactoryBean、SimpleTriggerBean、SchedulerFactoryBean,看看裏面需要注入什麼屬性即可明白了。Spring會在Spring容器啓動時候,啓動Quartz容器。
6、Quartz容器的關閉方式也很簡單,如果是Spring整合,則有兩種方法,一種是關閉Spring容器,一種是獲取到SchedulerFactoryBean實例,然後調用一個shutdown就搞定了。如果是Quartz獨立使用,則直接調用scheduler.shutdown(true);
7、Quartz的JobDetail、Trigger都可以在運行時重新設置,並且在下次調用時候起作用。這就爲動態作業的實現提供了依據。你可以將調度時間策略存放到數據庫,然後通過數據庫數據來設定Trigger,這樣就能產生動態的調度。
參考博客:
https://blog.51cto.com/lavasoft/181907
https://www.cnblogs.com/drift-ice/p/3817269.html
https://www.cnblogs.com/zhanghaoliang/p/7886110.html
https://blog.csdn.net/u010648555/article/category/6601767
https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/