上次學習了Timer,我們知道了在項目中使用Timer有很多不足之處,那麼對那些複雜的定時調度任務,我們應該怎麼處理呢? 這個時候Quartz就閃亮登場啦,它會幫我們解決這些難題;
1, 首先我們來說一下 Quartz 與 Timer 的區別:
(1), 出身不同, Timer 爲JDK自帶; Quartz 爲開源項目;
(2), 能力區別, Timer 遠沒有 Quartz 功能強大和完善, Timer 不能搞定的交給 Quartz ;
(3), 底層機制不同, Timer 利用後臺子線程實現, Quartz 可以用於線程池中;
2, Quartz簡介:
OpenSymphony提供的強大的開源任務調度框架;
官網: http://www.quartz-scheduler.org/
Maven倉庫:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
3, 好了,先來個HelloWorld:
(1),引入Jar包;(不用Maven的話需要引入: quartz-2.3.0.jar, slf4j-api-1.7.25.jar)
(2),創建任務:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
// TODO 具體的業務邏輯:
System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
}
}
(3),調度任務:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
// 創建一個 JobDetail 實例,將該實例與 HelloJob Class 綁定;
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob").build();
// 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1).repeatForever())
.build();
// 創建 Scheduler 實例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("當前時間:"+sf.format(calender.getTime()));
scheduler.start();
}
}
4, 現在我們再來分析一下上面的例子:
(1), 首先是我們要創建一個Job: HelloJob; Job的應用及生命週期:
/**
* (1),Job接口非常容易實現,只有一個execute方法,類似TimerTask的run方法,在裏面編寫業務邏輯;
* (2),Job實例在Quartz中的生命週期:
* 每次調度器執行Job時,它在調用execute方法前會創建一個新的Job實例.當調用完成後,關聯的Job對象實例會被釋放,之後會被垃圾回收;
**/
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
// TODO 具體的業務邏輯:
}
}
(2), 之後需要調用我們的HelloJob,那麼就有了JobDetail:
public class Test{
public static void main(String[] args) throws SchedulerException {
/**
* 創建一個 JobDetail 實例,將該實例與 HelloJob Class 綁定;
*
* JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,
* 它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
* 重要屬性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob","group1").build();
//我們可以通過 jobDetail.getKey() 來獲取這些屬性,方便以後調度;
System.out.println("jobDetail name is "+jobDetail.getKey().getName());
System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
}
}
5, 通過 JobExecutionContext 來獲取向 Job 中傳入的參數:
(1), 傳入參數:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
/**
* 創建一個 JobDetail 實例,將該實例與 HelloJob綁定;
*
* JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
* 重要屬性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
//傳入JobDetail的屬性及參數
.withIdentity("myJob","group1")
.usingJobData("message", "HelloJobDetail")
.usingJobData("FloatMessage", 2.2F)
.build();
//通過 getKey()獲取相關屬性;
System.out.println("jobDetail name is "+jobDetail.getKey().getName());
System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
// 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
Trigger trigger = TriggerBuilder.newTrigger()
//傳入Trigger(觸發器)的屬性及參數
.withIdentity("myTrigger")
.usingJobData("message", "HelloTrigger")
.usingJobData("DoubleMessage", 2.4D)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1).repeatForever())
.build();
// 創建 Scheduler 實例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("當前時間:"+sf.format(calender.getTime()));
scheduler.start();
}
}
(2), 獲取參數:
package quartz;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
public class HelloJob implements Job{
/**
* JobExecutionContext :
* (1), 當Scheduler(調度器) 調用一個Job,就會將JobExecutionContext傳遞給Job的execute()方法;
* (2), Job能通過JobExecutionContext對象訪問到Quartz運行時候的環境及Job本身的明細數據;
**/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//獲取傳入的任務屬性信息;
JobKey jobKey = context.getJobDetail().getKey();
System.out.println("Job(任務) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
//獲取傳入的觸發器屬性信息;
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("Trigger(觸發器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
/**
* JobDataMap:
* (1), 在進行任務調度時JobDataMap存儲,在JobExecutionContext中,非常方便獲取.
* (2), JobDataMap可以用來裝載任何可序列化的數據對象,當Job實例對象被執行時,這些參數對象會傳遞給它.
* (3), JobDateMap實現了JDK的Map接口,並且添加了一些非常方便的方法用來存取基本數據類型.
* 獲取方試:
* */
//獲取傳入的Job參數信息;
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
//獲取傳入的Trigger參數信息;
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
/* --------------獲取JobDataMap方法二: 合併獲取--------------- */
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage")+"\n");
/**
* 測試證明: 當Trigger與JobDetail中傳入的Key值相同,使用mergedJobDataMap獲取,則優先獲取Trigger中的Key值;JobDetail中的Key值被覆蓋掉;
* */
}
}
(3), 通過set,get方法獲取參數:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
/**
* (1),Job接口非常容易實現,只有一個execute方法,類似TimerTask的run方法,在裏面編寫業務邏輯;
* (2),Job實例在Quartz中的生命週期:
* 每次調度器執行Job時,它在調用execute方法前會創建一個新的Job實例.當調用完成後,關聯的Job對象實例會被釋放,之後會被垃圾回收;
**/
public class HelloJob implements Job{
private String message;
private float FloatMessage;
private double DoubleMessage;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public float getFloatMessage() {
return FloatMessage;
}
public void setFloatMessage(float floatMessage) {
FloatMessage = floatMessage;
}
public double getDoubleMessage() {
return DoubleMessage;
}
public void setDoubleMessage(double doubleMessage) {
DoubleMessage = doubleMessage;
}
/**
* JobExecutionContext是什麼:
* (1), 當Scheduler(調度器) 調用一個Job,就會將JobExecutionContext傳遞給Job的execute()方法;
* (2), Job能通過JobExecutionContext對象訪問到Quartz運行時候的環境及Job本身的明細數據;
**/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
// TODO 具體的業務邏輯:
System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
//獲取傳入的任務屬性信息;
JobKey jobKey = context.getJobDetail().getKey();
System.out.println("Job(任務) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
//獲取傳入的觸發器屬性信息;
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("Trigger(觸發器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
/**
* JobDataMap:
* (1), 在進行任務調度時JobDataMap存儲,在JobExecutionContext中,非常方便獲取.
* (2), JobDataMap可以用來裝載任何可序列化的數據對象,當Job實例對象被執行時,這些參數對象會傳遞給它.
* (3), JobDateMap實現了JDK的Map接口,並且添加了一些非常方便的方法用來存取基本數據類型.
* 獲取方試:
* */
//獲取傳入的Job參數信息;
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
//獲取傳入的Trigger參數信息;
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
/* --------------獲取JobDataMap方法二: 合併獲取--------------- */
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage"));
/**
* 測試證明: 當Trigger與JobDetail中傳入的Key值相同,使用mergedJobDataMap獲取,則優先獲取Trigger中的Key值;JobDetail中的Key值被覆蓋掉;
* */
/* --------------獲取JobDataMap方法三: 屬性 get,set--------------- */
System.out.println("ALL get param: "+message+", "+FloatMessage+", "+DoubleMessage+"\n");
/**
* Job實現類中創建setter方法對應JobDataMap的鍵值(Quartz框架默認的JobFactory實現類在初始化Job實例對象時會自動調用這些setter方法);
* */
}
}
6, Trigger 設置開始/結束 觸發時間:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
/**
* 創建一個 JobDetail 實例,將該實例與 HelloJob綁定;
*
* JobDetail爲Job實例提供了許多設置屬性,以及JobDataMap成員變量屬性,它用來儲存特定Job實例的狀態信息,調度器需要藉助JobDetail對象來添加Job實例.
* 重要屬性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("myJob","group1")
.usingJobData("message", "HelloJobDetail")
.usingJobData("FloatMessage", 2.2F)
.build();
// 創建一個Trigger實例,定義該job立即執行,並且每隔1秒鐘重複執行一次;
/**
* Trigger是Quartz中的出觸發器,用來告訴調度任務作業什麼時候觸發.即Trigger對象是用來觸發Job的;
* 通用屬性:
* JobKey: 表示Job實例的標識,觸發器被觸發時,該指定的Job實例會被執行;
* StartTime: 表示觸發器的時間表 首次被觸發的時間.值爲java.util.Date;
* EndTime: 指定觸發器的不在被觸發的時間.值的類型是java.util.Date;
* */
//開始執行時間爲當前時間的3秒後
Date startTime = new Date();
startTime.setTime(startTime.getTime()+3000);
//結束執行時間爲當前時間的6秒後
Date endTime = new Date();
endTime.setTime(endTime.getTime()+6000);
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.usingJobData("DoubleMessage", 2.4D)
//設置開始/結束時間
.startAt(startTime)
.endAt(endTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
// 創建 Scheduler 實例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("當前時間:"+sf.format(calender.getTime()));
scheduler.start();
}
}
Job中也可以獲取 開始/結束 時間:
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss");
// TODO 具體的業務邏輯:
System.out.println("Hellow Quartz! 執行時間:"+sf.format(calender.getTime()));
/* --------------------------獲取開始/結束時間--------------------- */
Trigger trigger = context.getTrigger();
Date startTime = trigger.getStartTime();
Date endTime = trigger.getEndTime();
System.out.println("StartTime is "+sf.format(startTime));
System.out.println("EndTime is "+sf.format(endTime)+"\n");
}
}
7, SimpleTrigger設置執行條件:
/**
* SimpleTrigger: 在一個指定時間段內執行一次任務,或在指定時間間隔內多次執行任務;
* 注意: (1)重複次數可以爲0,正整數或是 SimpleTrigger.REPEAT_INDEFINITELY 常量值.
* (2)重複執行時間間隔必須爲0或長整數;
* (3)一擔被指定了endTime參數,那麼它會覆蓋重複次數參數的效果;
* */
SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.usingJobData("DoubleMessage", 2.4D)
//設置開始/結束時間
.startAt(startTime)
.endAt(endTime)
//設置多次執行條件
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
//時間間隔,單位有: 秒, 毫秒, 分鐘, 小時
.withIntervalInSeconds(1)
//循環執行
// .repeatForever()
//執行次數, (參數+1)次, 參數爲0執行1次,參數爲1執行2次,...; 參數爲-1,則執行無窮
.withRepeatCount(-1)
)
.build();
到這我們已經對Quartz有了一定的認識,那麼下次再來分享更復雜的任務調度方式;看看怎麼在某個節日來調度的.