前一段時間項目需要做一個定時發送消息的功能,該功能依附于Web應用上,即當Web應用啟動時,該應用就開始作用。起先決定使用java.util.Timerjava.util.TimerTask來實現,但是研究了一下以后發現Java Timer的功能比較弱,而且其線程的范圍不受Web應用的約束。后來發現了Quartz這個開源的調度框架,非常有趣。

首先我們要得到Quartz的最新發布版。目前其最新的版本是1.6。我們可以從以下地址獲得它的完整下載包,包中可謂湯料十足,不僅有我們要的quartz.jar,更包含多個例程和詳細的文檔,從API到配置文件的XSD一應俱全。感興趣的朋友也可以在src目錄下找到該項目的源碼一看究竟。

廢話少說,下面就來看一看這個東東是怎么在Java Web Application中得以使用的。

首先不得不提出的是Quartz的三個核心概念:調度器、觸發器、作業。讓我們來看看他們是如何工作的吧。

一.作業總指揮——調度器

1. Scheduler接口

該接口或許是整個Quartz中最最上層的東西了,它提攜了所有觸發器和作業,使它們協調工作。每個Scheduler都存有JobDetailTrigger的注冊,一個Scheduler中可以注冊多個JobDetail和多個Trigger,這些JobDetailTrigger都可以通過group name和他們自身的name加以區分,以保持這些JobDetailTrigger的實例在同一個Scheduler內不會沖突。所以,每個Scheduler中的JobDetail的組名是唯一的,本身的名字也是唯一的(就好像是一個JobDetailID)。Trigger也是如此。

Scheduler實例由SchedulerFactory產生,一旦Scheduler實例生成后,我們就可以通過生成它的工廠來找到該實例,獲取它相關的屬性。下面的代碼為我們展示了如何從一個Servlet中找到SchedulerFactory并獲得相應的Scheduler實例,通過該實例,我們可以獲取當前作業中的testmode屬性,來判斷該作業是否工作于測試模式。

//從當前Servlet上下文中查找StdSchedulerFactory

??????????? ServletContext ctx=request.getSession().getServletContext();

??????????? StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute("org.quartz.impl.StdSchedulerFactory.KEY");

???????????

??????????? Scheduler sch = null;

??????????? try {

??????????????? //獲取調度器

??????????????? sch = factory.getScheduler("SchedulerName");

??????????????? //通過調度器實例獲得JobDetail,注意領會JobDetailNameGroupName的用法

??????????????? JobDetail jd=sch.getJobDetail("JobDetailName", "GroupName");

??????????????? Map jobmap1=jd.getJobDataMap();

??????????????? istest=jobmap1.get("testmode")+"";

??????????? } catch (Exception se) {

??????????????? //如果得不到當前作業,則從配置文件中讀取testmode

??????????????? ReadXML("job.xml").get(“job.testmode”);

??????????? }

?

Scheduler實例生成后,它處于"stand-by"模式,需要調用其start方法來使之投入運作。

public class SendMailShedule{

??? //設置標準SchedulerFactory

??? static SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

??? static Scheduler sched;

???

??? public static void run()throws Exception{

??????? //生成Scheduler實例

???????????? sched = schedFact.getScheduler();

??????? //創建一個JobDetail實例,對應的Job實現類是SendMailJob

???????????? JobDetail jobDetail = new JobDetail("myJob",sched.DEFAULT_GROUP,SendMailJob.class);

??????? //設置CronTrigger,利用Cron表達式設定觸發時間

??????? CronTrigger trigger = new CronTrigger("myTrigger","test","0 0 8 1 * ?");

??????? sched.scheduleJob(jobDetail, trigger);

??????? sched.start();

??? }

???

??? public static void? stop()throws Exception{

??????? sched.shutdown();

??? }

}

另外,我們也可以通過監聽器來跟蹤作業和觸發器的工作狀態。

二.作業及其相關

1. Job

作業實際上是一個接口,任何一個作業都可以寫成一個實現該接口的類,并實現其中的execute()方法,來完成具體的作業任務。

2. JobDetail

JobDetail可以指定我們作業的詳細信息,比如可以通過反射機制動態的加載某個作業的實例,可以指定某個作業在單個調度器內的作業組名稱和具體的作業名稱,可以指定具體的觸發器。

一個作業實例可以對應多個觸發器(也就是說學校每天10點放一次眼保健操錄音,下午3點半可以再放一次),但是一個觸發器只能對應一個作業實例(10點鐘的時候學校不可能同時播放眼保健操和廣播體操的錄音)。

3. JobDataMap

這是一個給作業提供數據支持的數據結構,使用方法和java.util.Map一樣,非常方便。當一個作業被分配給調度器時,JobDataMap實例就隨之生成。

Job有一個StatefulJob子接口,代表有狀態的任務,該接口是一個沒有方法的標簽接口,其目的是讓Quartz知道任務的類型,以便采用不同的執行方案。無狀態任務在執行時擁有自己的JobDataMap拷貝,對JobDataMap的更改不會影響下次的執行。而有狀態任務共享共享同一個JobDataMap實例,每次任務執行對JobDataMap所做的更改會保存下來,后面的執行可以看到這個更改,也即每次執行任務后都會對后面的執行發生影響。

正因為這個原因,無狀態的Job可以并發執行,而有狀態的StatefulJob不能并發執行,這意味著如果前次的StatefulJob還沒有執行完畢,下一次的任務將阻塞等待,直到前次任務執行完畢。有狀態任務比無狀態任務需要考慮更多的因素,程序往往擁有更高的復雜度,因此除非必要,應該盡量使用無狀態的Job

如果Quartz使用了數據庫持久化任務調度信息,無狀態的JobDataMap僅會在Scheduler注冊任務時保持一次,而有狀態任務對應的JobDataMap在每次執行任務后都會進行保存。

JobDataMap實例也可以與一個觸發器相關聯。這種情況下,對于同一作業的不同觸發器,我們可以在JobDataMap中添加不同的數據,以便作業在不同時間執行時能夠提供更為靈活的數據支持(學校上午放眼保健操錄音第一版,下午放第二版)。

不管是有狀態還是無狀態的任務,在任務執行期間對TriggerJobDataMap所做的更改都不會進行持久,也即不會對下次的執行產生影響。

?下一篇 Quartz調度框架應用總結(續1)

文章來源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!550.entry