定時任務,不管是互聯網公司仍是傳統的軟件行業都是必不可少的。Quartz,它是好多優秀的定時任務開源框架的基礎,使用它,咱們可使用最簡單基礎的配置來輕鬆的使用定時任務。java
Quartz 是 OpenSymphony 開源組織在 Job scheduling 領域的又一個開源項目,它能夠與 J2EE 與 J2SE 應用程序相結合也能夠單獨使用。web
Quartz 是開源且具備豐富特性的「任務調度庫」,可以集成與任何的 java 應用,小到獨立的應用,大至電子商業系統。Quartz 可以建立亦簡單亦複雜的調度,以執行上10、上百,甚至上萬的任務。任務 job 被定義爲標準的 java 組件,可以執行任何你想要實現的功能。Quartz 調度框架包含許多企業級的特性,如 JTA 事務、集羣的支持。spring
簡而言之,Quartz 就是基於 java 實現的任務調度框架,用於執行你想要執行的任何任務。apache
Quart 官網 : http://www.quartz-scheduler.org編程
Job 就是你想要實現的任務類,每個 Job 必須實現 org.quartz.job 接口,且只需實現接口 定義的 execute 方法。設計模式
Trigger 是爲你執行任務的觸發器,好比你想天天定時 3 點發送一封統計郵件,Trigger 將會設置 3 點執行該任務。tomcat
Trigger 主要包含兩種 SimpleTrigger 和 CronTrigger 兩種。服務器
Scheduler 是任務的調度器,它會將任務 Job 及觸發器 Trigger 整合起來,負責基於 Trigger 設定的時間來執行 Job。app
如下是 Quartz 編程 API 的幾個重要接口,也是 Quartz 的重要組件。框架
一、建立 maven java 項目,引入以下依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zze.quertz</groupId> <artifactId>quartz_test1</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--Quartz 依賴--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.0</version> </dependency> <!--整合 log4j--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <!--log4j 依賴--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <target>1.8</target> <source>1.8</source> </configuration> </plugin> </plugins> </build> </project>
二、建立任務類:
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; import java.util.Date; /** * 輸出當前時間任務類 */ public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { Date date = new Date(); String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
三、編寫執行類:
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { // 一、調度器(Scheduler),從工廠中獲取調度器實例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 二、任務實例(JobDetail) JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) // 加載任務類,與 HelloJob 完成綁定,需實現 org.quartz.Job 接口 .withIdentity("job1", "group1") // param1 : 任務的名稱(惟一標示),必須指定 param2 : 任務組名稱,未指定時默認爲 DEFAULT .build(); // 三、觸發器(Trigger) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // param1 : 觸發器名稱(惟一標示) param2 : 觸發器組名稱 .startNow() // 立刻啓動觸發器 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) // 每 5 秒重複執行 .build(); // 四、讓調度器關聯任務和觸發器,保證按照觸發器定義的條件執行任務 scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); // 啓動 // scheduler.shutdown(); // 中止 } }
String jobName = jobDetail.getKey().getName();// 任務名稱 String jobGroupName = jobDetail.getKey().getGroup();// 組名稱 String jobClassName = jobDetail.getJobClass().getName();// 任務類 System.out.println(jobName); // job1 System.out.println(jobGroupName); // group1 System.out.println(jobClassName); // com.zze.quartz.job.HelloJob
// 獲取 JobDetail 內容 JobDetail jobDetail = jobExecutionContext.getJobDetail(); // 獲取 Trigger 內容 Trigger trigger = jobExecutionContext.getTrigger(); // 獲取 Scheduler 內容 Scheduler scheduler = jobExecutionContext.getScheduler(); // 獲取當前 job 實例 Job jobInstance = jobExecutionContext.getJobInstance(); System.out.println(this == jobInstance); // true // 獲取下次執行時間 Date nextFireTime = jobExecutionContext.getNextFireTime(); // 獲取上次執行時間 Date previousFireTime = jobExecutionContext.getPreviousFireTime(); // 獲取當前執行時間 Date fireTime = jobExecutionContext.getFireTime(); // 獲取單次任務執行的惟一標示 String fireInstanceId = jobExecutionContext.getFireInstanceId(); // 獲取 JobDataMap JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap();
可經過 JobDetail 和 Trigger 傳遞數據給 Job 類,以下:
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("msg","hello job from jobDetail") // 傳遞參數到任務類 .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .usingJobData("msg","hello job from trigger") // 傳遞參數到任務類 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
Job 類接收數據有以下兩種方式:
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; /** * 輸出當前時間任務類 */ public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); // 經過 JobDetail 獲取 JobDataMap JobDataMap jobDetailJobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); String msgFromJobDetail = jobDetailJobDataMap.getString("msg"); System.out.println(msgFromJobDetail); // hello job from jobDetail // 經過 Trigger 獲取 JobDataMap JobDataMap triggerJobDataMap = jobExecutionContext.getTrigger().getJobDataMap(); String msgFromTrigger = triggerJobDataMap.getString("msg"); System.out.println(msgFromTrigger); // hello job from trigger } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; /** * 輸出當前時間任務類 */ public class HelloJob implements Job { private String msg; public void setMsg(String msg) { this.msg = msg; } @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); System.out.println(msg); // hello job from trigger } }
注意:經過方式二接收數據時,JobDetail 和 Trigger 傳遞的 key 值不可相同,不然 Trigger 傳遞的數據會覆蓋 JobDetail 傳遞的數據。
有狀態的 Job 能夠理解爲屢次 Job 調用期間能夠持有一些狀態信息,這些狀態信息存儲在 JobDataMap 中,而默認的無狀態 Job 每次調用都會建立一個新 JobDataMap。
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("count",0) // 傳遞參數到任務類 .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .usingJobData("msg","hello job from trigger") // 傳遞參數到任務類 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { private Integer count; public void setCount(Integer count) { this.count = count; } @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); System.out.println(count); count++; jobExecutionContext.getJobDetail().getJobDataMap().put("count", count); } }
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("count",0) // 傳遞參數到任務類 .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .usingJobData("msg","hello job from trigger") // 傳遞參數到任務類 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import java.text.SimpleDateFormat; @PersistJobDataAfterExecution // 給 Job 添加上該註解那麼該 Job 就變成了有狀態的Job,屢次調用 Job 會保存該 Job 的狀態 public class HelloJob implements Job { private Integer count; public void setCount(Integer count) { this.count = count; } @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); System.out.println(count); count++; jobExecutionContext.getJobDetail().getJobDataMap().put("count", count); } }
HelloJob 類沒有添加 @PersistJobDataAfterExecution 註解時,每次調用都會新建立一個 JobDataMap,count 不會累加。
HelloJob 類添加 @PersistJobDataAfterExecution 註解時,屢次 Job 調用期間會持久化 JobDataMap 信息,因此能夠實現 count 的累加。
Quartz 有一些不一樣的觸發器類型,不過,用得最多的是 SimpleTrigger 和 CronTrigger。
指定任務開始與結束的時間。
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException, ParseException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("count",0) // 傳遞參數到任務類 .build(); Date startTime = new Date(); Date endTime = new Date(); startTime.setTime(startTime.getTime()+3000); // 推遲 3 秒執行 endTime.setTime(startTime.getTime()+10000); // 推遲 10 秒結束執行 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") // .startNow() .startAt(startTime) // 設置開始的時間 .endAt(endTime) // 設置結束的時間 .usingJobData("msg","hello job from trigger") // 傳遞參數到任務類 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
package com.zze.quartz.job; import org.quartz.*; import java.text.SimpleDateFormat; import java.util.Date; @PersistJobDataAfterExecution // 給 Job 添加上該註解那麼該 Job 就變成了有狀態的Job,屢次調用 Job 會保存該 Job 的狀態 public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); Trigger trigger = jobExecutionContext.getTrigger(); JobKey jobKey = trigger.getJobKey(); Date startTime = trigger.getStartTime(); Date endTime = trigger.getEndTime(); System.out.printf("開始時間:%s\n", simpleDateFormat.format(startTime)); System.out.printf("結束時間:%s\n", simpleDateFormat.format(endTime)); } }
在 Quartz 中 SImpleTrigger 對於設置和使用是最爲簡單的一種 Trigger。
它是爲那種須要在特定的日期/時間啓動,且能以一個可能的間隔時間重複執行 n 次的 Job 所設計的。
讓一個任務在指定時間開始執行,而且只重複執行三次。
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.text.ParseException; import java.util.Date; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException, ParseException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); Date startTime = new Date(); startTime.setTime(startTime.getTime() + 3000); // 推遲 3 秒執行 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startAt(startTime) // 設置開始的時間 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5).withRepeatCount(3 - 1)) .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
若是你須要像日曆那樣按日程來出發任務,而不是像 SimpleTrigger 那樣每隔特定的間隔時間觸發,CronTrigger 一般比 SimpleTrigger 更有用,由於它是基於日曆的做業調度器。
使用 CronTrigger,你能夠指定例如「每週五中午 12:00」,或者「每隔工做日的 9:30」或「從每一個周1、周3、週五的上午 9:00 到 上午 10:00 之間每間隔 5 分鐘」這樣的日程安排來觸發。甚至,像 SimpleTrigger 同樣,CronTrigger 也有一個 startTime 以指定日程從何時開始,也有一個 endTime(可選)來指定日程什麼時候終止再也不繼續。
Cron 表達式用來配置 CronTrigger 實例。Cron 表達式是一個由 7 個子表達式組成的字符串。每一個子表達式都描述了一個單獨的日程細節。這些子表達式用空格分隔,分別表示:
Seconds Minutes Hours DayofMonth Month DayofWeek Year(可選)
各字段含義 | ||
---|---|---|
字段 | 容許值 | 容許的特殊字符 |
秒(Seconds) | 0~59的整數 | , - * / 四個字符 |
分(Minutes) | 0~59的整數 | , - * / 四個字符 |
小時(Hours) | 0~23的整數 | , - * / 四個字符 |
日期(DayofMonth) | 1~31的整數(可是你須要考慮你月的天數) | ,- * ? / L W C 八個字符 |
月份(Month) | 1~12的整數或者 JAN-DEC | , - * / 四個字符 |
星期(DayofWeek) | 1~7的整數或者 SUN-SAT (1=SUN) | , - * ? / L C # 八個字符 |
年(可選,留空)(Year) | 1970~2099 | , - * / 四個字符 |
每個域均可以使用數字,但還能夠出現以下特殊字符,它們的含義以下:
*:表示匹配該域的任意值。假如在 Minutes 域使用 * , 即表示每分鐘都會觸發事件。 ?:只能用在 DayofMonth 和 DayofWeek 兩個域。它也匹配域的任意值,但實際不會。由於 DayofMonth 和 DayofWeek 會相互影響。例如想在每個月的 20 日觸發調度,無論 20 日究竟是星期幾,則只能使用以下寫法: 13 13 15 20 * ?, 其中最後一位只能用 ? ,而不能使用 * ,若是使用 * 表示無論星期幾都會觸發,實際上並非這樣。 -:表示範圍。例如在 Minutes 域使用5-20,表示從5分到20分鐘每分鐘觸發一次 /:表示起始時間開始觸發,而後每隔固定時間觸發一次。例如在 Minutes 域使用 5/20,則意味着 5 分鐘觸發一次,而 25,45 等分別觸發一次. ,:表示列出枚舉值。例如:在 Minutes 域使用 5,20,則意味着在 5 和 20 分分別觸發一次。 L:表示最後,只能出如今 DayofWeek 和 DayofMonth 域。若是在 DayofWeek 域使用 5L,意味着在最後的一個星期四觸發。 W:表示有效工做日(週一到週五),只能出如今 DayofMonth 域,系統將在離指定日期的最近的有效工做日觸發事件。例如:在 DayofMonth 使用 5W,若是 5 日是星期六,則將在最近的工做日:星期五,即 4 日觸發。若是 5 日是星期天,則在 6 日(週一)觸發;若是 5 日在星期一到星期五中的一天,則就在 5 日觸發。另一點,W 的最近尋找不會跨過月份 。 LW:這兩個字符能夠連用,表示在某個月最後一個工做日,即最後一個星期五。 #:用於肯定每月第幾個星期幾,只能出如今 DayofMonth 域。例如 4#2,表示某月的第二個星期三。
經常使用表達式示例:
0 0 2 1 * ? * 表示在每個月的 1 日的凌晨 2 點 0 15 10 ? * MON-FRI 表示週一到週五天天上午 10:15 0 15 10 ? 6L 2002-2006 表示 2002-2006 年的每月的最後一個星期五上午 10:15 0 0 10,14,16 * * ? 天天上午 10 點,下午 2 點,4 點 0 0/30 9-17 * * ? 朝九晚五工做時間內每半小時 0 0 12 ? * WED 表示每一個星期三中午 12 點 0 0 12 * * ? 天天中午 12 點觸發 0 15 10 ? * * 天天上午 10:15 觸發 0 15 10 * * ? 天天上午 10:15 觸發 0 15 10 * * ? * 天天上午 10:15 觸發 0 15 10 * * ? 2005 2005 年的天天上午 10:15 觸發 0 * 14 * * ? 在天天下午 2 點到下午 2:59 期間的每 1 分鐘觸發 0 0/5 14 * * ? 在天天下午 2 點到下午 2:55 期間的每 5 分鐘觸發 0 0/5 14,18 * * ? 在天天下午 2 點到 2:55 期間和下午 6 點到 6:55 期間的每 5 分鐘觸發 0 0-5 14 * * ? 在天天下午 2 點到下午 2:05 期間的每 1 分鐘觸發 0 10,44 14 ? 3 WED 每一年三月的星期三的下午 2:10 和 2: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 2002-2005 2002 年至 2005 年的每個月的最後一個星期五上午 10:15 觸發 0 15 10 ? * 6#3 每個月的第三個星期五上午 10:15 觸發
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.text.ParseException; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException, ParseException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() // 使用 Cron 調度器 .withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * 6 3 ?")) // 3 月 6 號 每隔 3 秒觸發 .build(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
scheduler.start(); // 調度器啓動 scheduler.standby(); // 掛起 scheduler.shutdown(false); // 當即關閉調度器 scheduler.shutdown(true); // 等待全部任務執行完畢後再終止調度器
經過以前的學習已經知道,調度器實例是經過 StdSchedulerFactory.getDefaultScheduler() 建立。查看該部分源碼會發現,建立調度器時加載了以下屬性文件:
# 用來區分特定的調度器實例,能夠按照功能用途給調度器起名 org.quartz.scheduler.instanceName:DefaultQuartzScheduler # 這個屬性與前者同樣,容許任意字符串,但這個值必須在全部調度器實例中是惟一的,尤爲是再一個集羣環境中,做爲集羣的惟一 key。設爲 auto 即讓 Quartz 自動生成這個值 org.quartz.scheduler.instanceId:auto org.quartz.scheduler.rmi.export:false org.quartz.scheduler.rmi.proxy:false org.quartz.scheduler.wrapJobExecutionInUserTransaction:false #Quartz 自帶的線程池實現類,實現了 org.quartz.threadPool org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool # 處理 Job 的線程個數,至少爲 1 ,最多最好不要超過 100。 org.quartz.threadPool.threadCount:10 # 線程的優先級,優先級別高的線程比級別低的線程優先獲得執行。最小爲 1 ,最大爲 10,默認 爲 5 org.quartz.threadPool.threadPriority:5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread:true org.quartz.jobStore.misfireThreshold:60000 org.quartz.jobStore.class:org.quartz.simpl.RAMJobStore
咱們能夠在 classpath 下提供一個和上述同名的一個屬性文件來覆蓋 Quartz 的默認配置,也能夠經過以下方式手動加載配置文件來初始化調度器:
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(); // stdSchedulerFactory.initialize(String fileName); // stdSchedulerFactory.initialize(Properties properties); // stdSchedulerFactory.initialize(InputStream propertiesStream); Scheduler scheduler = stdSchedulerFactory.getScheduler();
Quartz 的監聽器用於在任務調度中你咱們所關注的事件發生時,可以及時獲取這一事件的通知。相似於任務執行過程當中的郵件、短信的提醒。Quartz 監聽器主要有 JobListener、TriggerListener、SchedulerListener 三種,顧名思義,分別表示任務、觸發器、調度器對應的監聽器,三者的使用方法相似。
而這幾種監聽器又分爲兩類,全局監聽器和非全局監聽器,兩者的區別在於:全局監聽器可以接收到全部的 Job/Trigger 的時間通知;而非全局監聽器只能接收到在其上註冊的 Job 或 Trigger 事件,不在其上註冊的 Job 或 Trigger 則不會監聽。
任務調度過程當中,與任務 Job 相關的事件包括:Job 要開始執行時、Job 執行完成時。要定義一個任務監聽器,須要實現 JobListener 接口:
package org.quartz; public interface JobListener { // 獲取 JobListener 的名稱 String getName(); // Scheduler 在 JobDetail 將要被執行時調用 void jobToBeExecuted(JobExecutionContext var1); // Scheduler 在 JobDetail 即將被執行,但又被 TriggerListener 否決時調用 void jobExecutionVetoed(JobExecutionContext var1); // Scheduler 在 JobDetail 被執行以後調用這個方法 void jobWasExecuted(JobExecutionContext var1, JobExecutionException var2); }
使用示例:
package com.zze.quartz.listener; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobListener; public class HelloJobListener implements JobListener { @Override public String getName() { return this.getClass().getSimpleName(); } @Override public void jobToBeExecuted(JobExecutionContext jobExecutionContext) { String jobName = jobExecutionContext.getJobDetail().getKey().getName(); System.out.printf("jobToBeExecuted---Job的名稱爲:%s,Scheduler 在 Job 將要被執行時調用該方法\n", jobName); } @Override public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) { String jobName = jobExecutionContext.getJobDetail().getKey().getName(); System.out.printf("jobExecutionVetoed---Job的名稱爲:%s,Scheduler 在 Job 即將被執行,但又被 TriggerListener 否決時調用該方法\n", jobName); } @Override public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) { String jobName = jobExecutionContext.getJobDetail().getKey().getName(); System.out.printf("jobExecutionVetoed---Job的名稱爲:%s,Scheduler 在 Job 執行完成以後調用該方法\n\n", jobName); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import com.zze.quartz.listener.HelloJobListener; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.KeyMatcher; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3)) .build(); scheduler.scheduleJob(jobDetail, trigger); // 註冊全局 Job 監聽器 // scheduler.getListenerManager().addJobListener(new HelloJobListener(), EverythingMatcher.allJobs()); // 註冊局部 Job 監聽器,對指定組、指定名稱的 Job 進行監聽 scheduler.getListenerManager().addJobListener(new HelloJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1"))); scheduler.start(); } }
任務調度過程當中,與觸發器 Trigger 相關的事件包括:觸發器觸發時、觸發器未正常觸發時、觸發器完成時等。要定義一個觸發器監聽器,須要實現 TriggerListener 接口:
package org.quartz; import org.quartz.Trigger.CompletedExecutionInstruction; public interface TriggerListener { // 獲取 Trigger 名稱 String getName(); // 當與監聽器相關聯的 Trigger 被觸發,Job 上的 execute() 方法將被執行時,Scheduler 就調用該方法。 void triggerFired(Trigger var1, JobExecutionContext var2); // 在 Trigger 觸發後,Job 將要被執行時由 Scheduler 調用該方法。TriggerListener 給了一個選擇去否決 Job 的執行。假如這個方法返回 true,這個 Job 將不會爲這次 Trigger 觸發而獲得執行。 boolean vetoJobExecution(Trigger var1, JobExecutionContext var2); // Scheduler 調用這個方法是在 Trigger 錯過觸發時。 void triggerMisfired(Trigger var1); // Trigger 被觸發而且完成了 Job 的執行時,Scheduler 調用這個方法。 void triggerComplete(Trigger var1, JobExecutionContext var2, CompletedExecutionInstruction var3); }
使用示例:
package com.zze.quartz.listener; import org.quartz.JobExecutionContext; import org.quartz.Trigger; import org.quartz.TriggerListener; public class HelloTriggerListener implements TriggerListener { @Override public String getName() { return this.getClass().getSimpleName(); } @Override public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) { String triggerName = trigger.getKey().getName(); System.out.printf("triggerFired---%s 將要被觸發\n", triggerName); } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) { String triggerName = trigger.getKey().getName(); boolean isTrigger = false; String msg = !isTrigger ? "容許被觸發" : "拒絕被觸發"; System.out.printf("vetoJobExecution---%s %s\n", triggerName, msg); return isTrigger; // 若是返回 true,將不會執行 Job 的方法。 } @Override public void triggerMisfired(Trigger trigger) { String triggerName = trigger.getKey().getName(); System.out.printf("triggerMisfired---%s 錯過觸發\n", triggerName); } @Override public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) { String triggerName = trigger.getKey().getName(); System.out.printf("triggerComplete---%s 觸發完成\n\n", triggerName); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import com.zze.quartz.listener.HelloTriggerListener; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.EverythingMatcher; import org.quartz.impl.matchers.KeyMatcher; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3)) .build(); scheduler.scheduleJob(jobDetail, trigger); // 註冊一個全局的 Trigger 監聽器 // scheduler.getListenerManager().addTriggerListener(new HelloTriggerListener(), EverythingMatcher.allTriggers()); // 註冊一個局部的 Trigger 監聽器 scheduler.getListenerManager().addTriggerListener(new HelloTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1"))); scheduler.start(); } }
任務調度過程當中,與調度器有關的事件包括:增長一個 Job/Trigger、刪除一個 Job/Trigger、Scheduler 發生錯誤、關閉 Scheduler 等。要定義一個調度器監聽器,須要實現 SchedulerListener 接口:
package org.quartz; public interface SchedulerListener { // 部署 JobDetail 時調用 void jobScheduled(Trigger trigger); // 卸載 JobDetail 時調用 void jobUnscheduled(TriggerKey triggerKey); // 當一個 Trigger 不再會觸發時調用,若這個觸發器對應的 Job 沒有狀態持久化,則將會從 Scheduler 中移除 void triggerFinalized(Trigger trigger); // 在一個 Trigger 被暫停時 void triggerPaused(TriggerKey triggerKey); // 在一個 TriggerGroup 被暫停時 void triggersPaused(String triggerGroup); // 在一個 Trigger 恢復時 void triggerResumed(TriggerKey triggerKey); // 在一個 TriggerGroup 恢復時 void triggersResumed(String var1); // 當新加入一個 Job 時 void jobAdded(JobDetail jobDetail); // 當移除一個 Job 時 void jobDeleted(JobKey jobKey); // 當 Job 暫停時 void jobPaused(JobKey jobKey); // 當 JobGroup 暫停時 void jobsPaused(String jobGroup); // 當 Job 恢復時 void jobResumed(JobKey jobKey); // 當 JobGroup 恢復時 void jobsResumed(String jobGroup); // 當 Scheduler 運行期出現錯誤時 void schedulerError(String msg, SchedulerException cause); // 當 Scheduler 掛起時 void schedulerInStandbyMode(); // 當 Scheduler 開始後 void schedulerStarted(); // 當 Scheduler 開始時 void schedulerStarting(); // 當 Scheduler 關閉後 void schedulerShutdown(); // 當 Scheduler 關閉時 void schedulerShuttingdown(); // 當 Scheduler 中數據被清除時 void schedulingDataCleared(); }
使用示例:
package com.zze.quartz.listener; import org.quartz.*; public class HelloSchedulerListener implements SchedulerListener { @Override public void jobScheduled(Trigger trigger) { String name = trigger.getKey().getName(); System.out.println(name + "完成部署"); } @Override public void jobUnscheduled(TriggerKey triggerKey) { String name = triggerKey.getName(); System.out.println(name + "完成卸載"); } @Override public void triggerFinalized(Trigger trigger) { System.out.println(trigger.getKey().getName() + "觸發器已移除"); } @Override public void triggerPaused(TriggerKey triggerKey) { System.out.println(triggerKey.getName() + "觸發器被暫停"); } @Override public void triggersPaused(String triggerGroup) { System.out.println(triggerGroup + "觸發器組被暫停"); } @Override public void triggerResumed(TriggerKey triggerKey) { System.out.println(triggerKey.getName() + "觸發器恢復"); } @Override public void triggersResumed(String triggerGroup) { System.out.println(triggerGroup + "觸發器組恢復"); } @Override public void jobAdded(JobDetail jobDetail) { System.out.println(jobDetail.getKey().getName() + "任務已添加"); } @Override public void jobDeleted(JobKey jobKey) { System.out.println(jobKey.getName() + "任務已刪除"); } @Override public void jobPaused(JobKey jobKey) { System.out.println(jobKey.getName() + "任務已暫停"); } @Override public void jobsPaused(String jobGroup) { System.out.println(jobGroup + "任務組已暫停"); } @Override public void jobResumed(JobKey jobKey) { System.out.println(jobKey.getName() + "任務已恢復"); } @Override public void jobsResumed(String jobGroup) { System.out.println(jobGroup + "任務組已恢復"); } @Override public void schedulerError(String msg, SchedulerException cause) { System.out.println("調度器發生錯誤:"+msg); } @Override public void schedulerInStandbyMode() { System.out.println("調度器掛起"); } @Override public void schedulerStarted() { System.out.println("調度器已開始"); } @Override public void schedulerStarting() { System.out.println("調度器正在開始"); } @Override public void schedulerShutdown() { System.out.println("調度器關閉"); } @Override public void schedulerShuttingdown() { System.out.println("調度器關閉中"); } @Override public void schedulingDataCleared() { System.out.println("調度器數據被清理"); } }
package com.zze.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime()); System.out.println("正在執行 xxx 任務,時間爲" + dateStr); } }
package com.zze.quartz.main; import com.zze.quartz.job.HelloJob; import com.zze.quartz.listener.HelloSchedulerListener; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class HelloSchedulerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3)) .build(); scheduler.scheduleJob(jobDetail, trigger); // 註冊調度器監聽 scheduler.getListenerManager().addSchedulerListener(new HelloSchedulerListener()); scheduler.start(); } }
建立 maven web 工程,引入如下依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zze.quartz</groupId> <artifactId>quartz_spring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
加載 Spring 核心配置文件:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
一、建立任務類:
package com.zze.quartz;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HelloJob {
public void run() {
System.out.println("Hello quartz! now ->" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
二、Spring 配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 定時任務的bean --> <bean id="helloJob" class="com.zze.quartz.HelloJob"/> <bean id="testQuartzJob_jd" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="helloJob"/> </property> <property name="targetMethod" value="run"></property> </bean> <!-- 調度的配置&job的配置 --> <bean id="testQuartzJob_ct" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail"> <ref bean="testQuartzJob_jd"/> </property> <!--每五秒執行--> <property name="cronExpression" value="*/5 * * * * ?"></property> </bean> <!-- 開啓定時任務--> <bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="testQuartzJob_ct"/> </list> </property> </bean> </beans>
三、啓動項目,觀察控制檯:
一、建立任務類並使用註解:
package com.zze.quartz; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; @Component public class HelloJob { @Scheduled(cron = "*/1 * * * * ?") // 每秒執行 public void run() { System.out.println("Hello quartz! now ->" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } }
二、配置 Spring 包掃描:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <!-- task任務掃描註解 --> <task:annotation-driven/> <!-- 掃描位置 --> <context:component-scan base-package="com.zze.quartz"/> </beans>
三、啓動項目,觀察控制檯: