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

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

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

首先不得不提出的是Quartz的三個核心概念:調(diào)度器、觸發(fā)器、作業(yè)。讓我們來看看他們是如何工作的吧。

一.作業(yè)總指揮——調(diào)度器

1. Scheduler接口

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

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

//從當(dāng)前Servlet上下文中查找StdSchedulerFactory

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

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

???????????

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

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

??????????????? //獲取調(diào)度器

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

??????????????? //通過調(diào)度器實例獲得JobDetail,注意領(lǐng)會JobDetailNameGroupName的用法

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

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

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

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

??????????????? //如果得不到當(dāng)前作業(yè),則從配置文件中讀取testmode

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

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

?

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

public class SendMailShedule{

??? //設(shè)置標(biāo)準(zhǔn)SchedulerFactory

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

??? static Scheduler sched;

???

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

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

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

??????? //創(chuàng)建一個JobDetail實例,對應(yīng)的Job實現(xiàn)類是SendMailJob

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

??????? //設(shè)置CronTrigger,利用Cron表達(dá)式設(shè)定觸發(fā)時間

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

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

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

??? }

???

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

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

??? }

}

另外,我們也可以通過監(jiān)聽器來跟蹤作業(yè)和觸發(fā)器的工作狀態(tài)。

二.作業(yè)及其相關(guān)

1. Job

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

2. JobDetail

JobDetail可以指定我們作業(yè)的詳細(xì)信息,比如可以通過反射機(jī)制動態(tài)的加載某個作業(yè)的實例,可以指定某個作業(yè)在單個調(diào)度器內(nèi)的作業(yè)組名稱和具體的作業(yè)名稱,可以指定具體的觸發(fā)器。

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

3. JobDataMap

這是一個給作業(yè)提供數(shù)據(jù)支持的數(shù)據(jù)結(jié)構(gòu),使用方法和java.util.Map一樣,非常方便。當(dāng)一個作業(yè)被分配給調(diào)度器時,JobDataMap實例就隨之生成。

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

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

如果Quartz使用了數(shù)據(jù)庫持久化任務(wù)調(diào)度信息,無狀態(tài)的JobDataMap僅會在Scheduler注冊任務(wù)時保持一次,而有狀態(tài)任務(wù)對應(yīng)的JobDataMap在每次執(zhí)行任務(wù)后都會進(jìn)行保存。

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

不管是有狀態(tài)還是無狀態(tài)的任務(wù),在任務(wù)執(zhí)行期間對TriggerJobDataMap所做的更改都不會進(jìn)行持久,也即不會對下次的執(zhí)行產(chǎn)生影響。

?下一篇 Quartz調(diào)度框架應(yīng)用總結(jié)(續(xù)1)

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