quartz 中JobExecutionContext的使用 - garnettcwm的專欄 - CSDN diana garnett 是誰
于是便有Quartz。不過,Quartz太久沒有更新了,而且它太復(fù)雜。由于我的系統(tǒng)是基于Spring構(gòu)建的,所以我希望能使用Spring支持的scheduling類庫,可惜Spring只支持commonj和Quartz,正確來說,在Java界,并沒有別的scheduling類庫了,而commonj只是一個(gè)interface,沒有具體的實(shí)現(xiàn),似乎在Weblogic之類的里面有實(shí)現(xiàn)。
當(dāng)然,也有另外一個(gè)選擇,也是輕量級的腳本語言常用的做法,就是使用Linux的crontable,可以實(shí)現(xiàn)比較復(fù)雜的定時(shí)。不過,腳本語言調(diào)用數(shù)據(jù)庫并不是很方便(應(yīng)該說我們的團(tuán)隊(duì)技術(shù)累積上的問題),如果用crontable啟動(dòng)Java,每次啟動(dòng)的成本又比較高。
在評估過各種方案之后,我還是選擇了使用Quartz,首先從Spring的輔助類開始入手吧。
題外話,在一個(gè)集群的環(huán)境里面(也就是多個(gè)Tomcat的環(huán)境下),定時(shí)任務(wù)應(yīng)該是獨(dú)立的應(yīng)用,也就是不應(yīng)該在每一個(gè)Tomcat里面都啟動(dòng)Quartz或者定時(shí)線程。另外,在Tomcat的應(yīng)用里面,也是盡量不要使用線程,有可能一點(diǎn)點(diǎn)小錯(cuò)誤就會(huì)導(dǎo)致整個(gè)Tomcat崩潰(其實(shí)我們還是使用很多的,呵呵)。
根據(jù)Quartz的使用行為,一個(gè)任務(wù)我們至少需要一個(gè)Job、一個(gè)JobDetail、一個(gè)Trigger(真復(fù)雜)
viewplaincopyto clipboardprint?
JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span><span>"myJob"</span>,<span>//jobname</span>
sched.<span>DEFAULT_GROUP</span>,<span>//jobgroup</span>
DumbJob.<span>class</span><span>)</span><span>;</span><span>//thejavaclasstoexecute</span>
Triggertrigger<span>=</span>TriggerUtils.<span>makeDailyTrigger</span><span>(</span><span>8</span>,<span>30</span><span>)</span><span>;</span>
trigger.<span>setStartTime</span><span>(</span><span>new</span><span>Date</span><span>(</span><span>)</span><span>)</span><span>;</span>
trigger.<span>setName</span><span>(</span><span>"myTrigger"</span><span>)</span><span>;</span>
sched.<span>scheduleJob</span><span>(</span>jobDetail,trigger<span>)</span><span>;</span>
JobDetail jobDetail = new JobDetail("myJob", // job namesched.DEFAULT_GROUP, // job group DumbJob.class);// the java class to executeTrigger trigger = TriggerUtils.makeDailyTrigger(8, 30);trigger.setStartTime(new Date());trigger.setName("myTrigger");sched.scheduleJob(jobDetail, trigger);
首先?。∥以谶@里要明確一個(gè)事情。Job類是沒有狀態(tài)的?。?br>
這是什么概念呢,就是說,你實(shí)現(xiàn)的一個(gè)Job(例如上面的代碼的DumbJob),并不是由你自己new出來的,留意一下newJobDetail的代碼,傳入的參數(shù)是DumbJob.class,而不是一個(gè)具體的job實(shí)例。Quartz幫你吧Jobnew一份出來,并且調(diào)用相應(yīng)的接口,并沒有別的功能。
這里會(huì)帶來一個(gè)什么問題呢,我們先來看看Spring的輔助類。
Spring有兩個(gè)輔助類可以產(chǎn)生JobDetail類,需要留意的是,Spring并不輔助產(chǎn)生Job類,也就是Spring認(rèn)為Job類不需要管理。
我們先看看第一個(gè),JobDetailBean
viewplaincopyto clipboardprint?
<span><span><bean</span><span>name</span>=<span>"exampleJob"</span><span>class</span>=<span>"org.springframework.scheduling.quartz.JobDetailBean"</span><span>></span></span>
<span><span><property</span><span>name</span>=<span>"jobClass"</span><span>value</span>=<span>"example.ExampleJob"</span><span>/></span></span>
<span><span><property</span><span>name</span>=<span>"jobDataAsMap"</span><span>></span></span>
<span><span><map<span>></span></span></span>
<span><span><entry</span><span>key</span>=<span>"timeout"</span><span>value</span>=<span>"5"</span><span>/></span></span>
<span><span></map<span>></span></span></span>
<span><span></property<span>></span></span></span>
<span><span></bean<span>></span></span></span>
<bean name="exampleJob"><property name="jobClass" value="example.ExampleJob" /><property name="jobDataAsMap"><map><entry key="timeout" value="5" /></map></property></bean>
不知道大家有沒有看出問題在哪里。propertyjobClass是一個(gè)類名,并不是一個(gè)實(shí)例名!也就是跟Quartz的調(diào)用一樣,是Quartz負(fù)責(zé)幫你new一個(gè)example.ExampleJob類出來,也就是說你不能對Job類進(jìn)行任何形式的注入(IOC),比如說,我們的example.ExampleJob是一個(gè)DAO,需要傳入DataSource進(jìn)行DB操作,沒轍。
因此,Spring提供了另外一個(gè)JobDetail輔助類MethodInvokingJobDetailFactoryBean
viewplaincopyto clipboardprint?
<span><span><bean</span><span>id</span>=<span>"jobDetail"</span><span>class</span>=<span>"org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"</span><span>></span></span>
<span><span><property</span><span>name</span>=<span>"targetObject"</span><span>ref</span>=<span>"exampleBusinessObject"</span><span>/></span></span>
<span><span><property</span><span>name</span>=<span>"targetMethod"</span><span>value</span>=<span>"doIt"</span><span>/></span></span>
<span><span></bean<span>></span></span></span>
<bean id="jobDetail"><property name="targetObject" ref="exampleBusinessObject" /><property name="targetMethod" value="doIt" /></bean>
你可以留意到,property targetObject是一個(gè)ref,指向的是一個(gè)常規(guī)的Spring管理的Bean。
但是!
MethodInvokingJobDetailFactoryBean很不友好。首先,它是通過反射調(diào)用的,而不是Interface,因此我們必須要看了Spring的xml才能知道誰被調(diào)用了,你還可能會(huì)寫一大堆property targetMethod=doIt,而且JobInterface是會(huì)傳入一個(gè)JobExecutionContext,這個(gè)被miss了。
其次,如果我們需要大量的Job的話(因?yàn)槲揖褪亲鲆粋€(gè)專門用來定時(shí)的應(yīng)用),Spring的配置文件會(huì)變得非常臃腫,我希望Job和JobDetail不需要Spring專門管理,只要他是一個(gè)Spring管理的Bean,并且實(shí)現(xiàn)了Job這個(gè)接口就ok了。
這里補(bǔ)充一個(gè)事情,我們跳過了Trigger的部分,每一個(gè)JobDetail必須配備一個(gè)相應(yīng)的Trigger,因此配置文件是你之前想象中的兩倍那么大,而且你還得給每一個(gè)Bean命名一個(gè)ID,而這個(gè)類你以后都不會(huì)用到。
我的目標(biāo)是:
1、只要是實(shí)現(xiàn)了Job接口的Spring管理的Bean,自動(dòng)加入scheduling,根本不用關(guān)心JobDetail的存在,也不會(huì)有注入的問題
2、所有Job均使用CronTrigger,并且通過配置文件設(shè)定Cron Expressions
通過研究MethodInvokingJobDetailFactoryBean和Quartz的代碼,我明白到JobDetail是有狀態(tài)的,而MethodInvokingJobDetailFactoryBean正是利用這點(diǎn)來實(shí)現(xiàn)具體效果的,于是便有了我一下這些輔助代碼
首先
,需要一個(gè)DummyJob,由于Quartz的主入口始終是Job類
viewplaincopyto clipboardprint?
<span>public</span><span>class</span>DummyJob<span>implements</span>Job<span>{</span>
span>public</span><span>void</span>execute<span>(</span>JobExecutionContextcontext<span>)</span>
<span>throws</span>JobExecutionException<span>{</span>
Jobjob<span>=</span><span>(</span>Job<span>)</span>context.<span>getMergedJobDataMap</span><span>(</span><span>)</span>.<span>get</span><span>(</span><span>"methodInvoker"</span><span>)</span><span>;</span>
<span>if</span><span>(</span>job<span>!=</span><span>null</span><span>)</span><span>{</span>
job.<span>execute</span><span>(</span>context<span>)</span><span>;</span>
<span>}</span>
span>}</span>
<span>}</span>
public class DummyJob implements Job {public void execute(JobExecutionContext context)throws JobExecutionException {Job job = (Job) context.getMergedJobDataMap().get("methodInvoker");if (job != null) {job.execute(context);}}}
jobDataMap就是JobDetail存儲狀態(tài)的地方,DummyJob唯一要做的就是,知道實(shí)際的Job類,并且調(diào)用它
接下來是戲玉了
viewplaincopyto clipboardprint?
Map<span><</span>String,Job<span>></span>jobMap<span>=</span>context.<span>getBeansOfType</span><span>(</span>Job.<span>class</span><span>)</span><span>;</span>
<span>for</span><span>(</span><span>Map</span>.<span>Entry</span><span><</span>String,Job<span>></span>entry<span>:</span>jobMap.<span>entrySet</span><span>(</span><span>)</span><span>)</span><span>{</span>
<span>String</span>taskName<span>=</span>entry.<span>getKey</span><span>(</span><span>)</span><span>;</span>
<span>String</span>cronExpression<span>=</span>props.<span>getProperty</span><span>(</span>taskName<span>)</span><span>;</span>
<span>if</span><span>(</span>cronExpression<span>==</span><span>null</span><span>)</span><span>{</span>
logger.<span>warn</span><span>(</span><span>"[{}]don'thaveacronExpression"</span>,taskName<span>)</span><span>;</span>
<span>continue</span><span>;</span>
<span>}</span>
<span>try</span><span>{</span>
Triggertrigger<span>=</span><span>new</span>CronTrigger<span>(</span>taskName<span>+</span><span>"Trigger"</span>,<span>null</span>,
cronExpression<span>)</span><span>;</span>
JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span>taskName<span>+</span><span>"Job"</span>,<span>null</span>,
DummyJob.<span>class</span><span>)</span><span>;</span>
jobDetail.<span>getJobDataMap</span><span>(</span><span>)</span>
.<span>put</span><span>(</span><span>"methodInvoker"</span>,entry.<span>getValue</span><span>(</span><span>)</span><span>)</span><span>;</span>
scheduler.<span>scheduleJob</span><span>(</span>jobDetail,trigger<span>)</span><span>;</span>
<span>}</span><span>catch</span><span>(</span><span>ParseException</span>e<span>)</span><span>{</span>
logger.<span>error</span><span>(</span><span>""</span>,e<span>)</span><span>;</span>
<span>}</span><span>catch</span><span>(</span>SchedulerExceptione<span>)</span><span>{</span>
logger.<span>error</span><span>(</span><span>""</span>,e<span>)</span><span>;</span>
<span>}</span>
<span>}</span>
Map<String, Job> jobMap = context.getBeansOfType(Job.class);for (Map.Entry<String, Job> entry : jobMap.entrySet()) {String taskName = entry.getKey();String cronExpression = props.getProperty(taskName);if (cronExpression == null) {logger.warn("[{}] don't have a cronExpression", taskName);continue;}try {Trigger trigger = new CronTrigger(taskName + "Trigger", null,cronExpression);JobDetail jobDetail = new JobDetail(taskName + "Job", null,DummyJob.class);jobDetail.getJobDataMap().put("methodInvoker", entry.getValue());scheduler.scheduleJob(jobDetail, trigger);} catch (ParseException e) {logger.error("", e);} catch (SchedulerException e) {logger.error("", e);}}
從Spring context里面讀取所有實(shí)現(xiàn)了Job的類遍歷,props是從文件里面讀取相應(yīng)的cronExpression配置。
viewplaincopyto clipboardprint?
JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span>taskName<span>+</span><span>"Job"</span>,<span>null</span>,
DummyJob.<span>class</span><span>)</span><span>;</span>
jobDetail.<span>getJobDataMap</span><span>(</span><span>)</span>
.<span>put</span><span>(</span><span>"methodInvoker"</span>,entry.<span>getValue</span><span>(</span><span>)</span><span>)</span><span>;</span>
JobDetail jobDetail = new JobDetail(taskName + "Job", null,DummyJob.class);jobDetail.getJobDataMap().put("methodInvoker", entry.getValue());
這兩句是關(guān)鍵
于是,Quartz變得更sexy了
quartz 中JobExecutionContext的使用
假如execute方法中需要一些額外的數(shù)據(jù)怎么辦?比如說execute
中希望發(fā)送一封郵件,但是我需要知道郵件的發(fā)送者、接收者等信息?
存在兩種解決方案:
1.JobDataMap類:
每個(gè)JobDetail都關(guān)聯(lián)了一個(gè)JobDataMap實(shí)例,JobDataMap是java.util.Map的子類,基本上是提供key-value形式的數(shù)據(jù),并提供了一些便利方法(主要是對java基本數(shù)據(jù)類型的支持,如put(String key,intvalue)),當(dāng)開發(fā)人員創(chuàng)建JobDetail的時(shí)候,可以把附加信息放到JobDataMap中,那么在execute方法中可以根據(jù)key找到需要的值。
JobDetail job = new JobDetail....
job.getJobDataMap().put("from","snowway@vip.sina.com");
...
在execute中
String from =jobExecutionContext.getJobDetail().getJobDataMap().getString("from");
....
不過,當(dāng)你使用數(shù)據(jù)庫存儲JobDetail的時(shí)候(默認(rèn)情況下使用RAM),這里有一個(gè)致命的弱點(diǎn),你不能把沒有實(shí)現(xiàn)java.io.Serializable的對象放入JobDataMap中,因?yàn)镼uartz將使用Blob字段保存(也可以通過配置文件關(guān)閉)序列化過的JobDataMap中的對象。比如你在execute方法中需要一個(gè)java.sql.Connection接口實(shí)例,這種情況也是普遍的,那么通常情況下你不能把Connection放入JobDataMap,即使你只想在execute中使用。(注:讀者可暫時(shí)認(rèn)為上面這段話是正確的,然而可以通過指示quartz改變這種行為,那屬于高級話題)
2.假如你需要一個(gè)java.sql.Connection,用于在execute中完成某些操作,那么你可以把Connection放入Quartz的SchedulerContext中,execute也可以訪問,并且Quartz不會(huì)持久化SchedulerContext中的任何東西。
scheduler.getContext().put("java.sql.Connection",connection);
execute中
Connection con =(Connection)jobExecutionContext.getScheduler().getContext().get("java.sql.Connection");
更多閱讀

厲以寧老師的詩詞 厲以寧是誰的老師
今天接到大學(xué)班主任王其文老師的郵件,給我們發(fā)來了《厲以寧教授誕辰80周年暨從教55周年征文》修改后的新版本。讀著這些文字,記憶的浪排排而來。我的那些恩師們和學(xué)長們、同學(xué)們的回憶真的是字字真切,感懷萬千。等正式出版的時(shí)候,大家會(huì)

誰知道伍佰《突然的自我》女聲版是誰唱的 突然的自我 伍佰mv
下午在虎跑路上,收音機(jī)傳來一首歌,伍佰的《突然的自我》,有點(diǎn)沙啞的女聲,有點(diǎn)像柯以敏。太動(dòng)聽了,不知道是誰唱的。突然的自我 伍佰那就不要留時(shí)光一過不再有你遠(yuǎn)眺的天空 掛更多的彩虹我會(huì)緊緊的 將你豪情放在心頭在寒冬時(shí)候 就回憶

黃河故人的核武器系列 黃河故人是誰
saintfei:從茶館和西西河網(wǎng)站收集整理的文章,感謝“黃河故人”貢獻(xiàn)的一系列精彩文章,向?yàn)橹袊暮耸聵I(yè)貢獻(xiàn)了青春、事業(yè)、名望甚至生命的人致敬。你們是真正的民族英雄。第一章 中國周邊國家核武器能力分析核武器應(yīng)該不算武器,屬于政治

CarlesRexach,決定梅西命運(yùn)的人 性格決定命運(yùn)是誰說的
當(dāng)初他來巴薩試訓(xùn)最后決定收下他的是Carles Rexach(國家德比后梅西還問候了他)。CarlesRexach只是用了很短的時(shí)間就確定了巴薩需要簽下梅西,他當(dāng)時(shí)面臨的是很多人的反對。反對聲音中,一個(gè)最主要的就是他有生長發(fā)育

世界上的第一臺電子計(jì)算機(jī)是誰發(fā)明的? 電子計(jì)算機(jī)發(fā)明那一年
世界上的第一臺電子計(jì)算機(jī)是誰發(fā)明的?中國的教科書、絕大多數(shù)學(xué)術(shù)著作和科學(xué)普及著作所說的電子計(jì)算機(jī)發(fā)明人都不是真正的發(fā)明人。真正的發(fā)明人是美國人約翰·阿塔那索夫(Atanasoff)教授。大多數(shù)書上說,美國籍匈牙利裔科學(xué)家馮·諾依曼(J
愛華網(wǎng)