Quartz中的Job與JobDetail解析
前言
你定義了一個(gè)實(shí)現(xiàn)Job接口的類,這個(gè)類僅僅表明該job需要完成什么類型的任務(wù)
除此之外,Quartz還需要知道該Job實(shí)例所包含的屬性;這將由JobDetail類來完成。
Job
讓我們先看看Job的特征(nature)以及Job實(shí)例的生命期。不妨先回頭看看第1課中的代碼片段:
// define the job and tie it to our HelloJob class JobDetail job = newJob(HelloJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .build(); // Trigger the job to run now, and then every 40 seconds Trigger trigger = newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(40) .repeatForever()) .build(); // Tell quartz to schedule the job using our trigger sched.scheduleJob(job, trigger);
現(xiàn)在考慮這樣定義的作業(yè)類“HelloJob”:
public class HelloJob implements Job { public HelloJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { System.err.println("Hello! HelloJob is executing."); } }
可以看到,我們傳給scheduler一個(gè)JobDetail實(shí)例,因?yàn)槲覀冊(cè)趧?chuàng)建JobDetail時(shí),將要執(zhí)行的job的類名傳給了JobDetail,所以scheduler就知道了要執(zhí)行何種類型的job;每次當(dāng)scheduler執(zhí)行job時(shí),在調(diào)用其execute(…)方法之前會(huì)創(chuàng)建該類的一個(gè)新的實(shí)例;執(zhí)行完畢,對(duì)該實(shí)例的引用就被丟棄了,實(shí)例會(huì)被垃圾回收;這種執(zhí)行策略帶來的一個(gè)后果是,job必須有一個(gè)無參的構(gòu)造函數(shù)(當(dāng)使用默認(rèn)的JobFactory時(shí));另一個(gè)后果是,在job類中,不應(yīng)該定義有狀態(tài)的數(shù)據(jù)屬性,因?yàn)樵趈ob的多次執(zhí)行中,這些屬性的值不會(huì)保留。
那么如何給job實(shí)例增加屬性或配置呢?如何在job的多次執(zhí)行中,跟蹤job的狀態(tài)呢?答案就是:JobDataMap,JobDetail對(duì)象的一部分。
JobDataMap
JobDataMap中可以包含不限量的(序列化的)數(shù)據(jù)對(duì)象,在job實(shí)例執(zhí)行的時(shí)候,可以使用其中的數(shù)據(jù);JobDataMap是Java Map接口的一個(gè)實(shí)現(xiàn),額外增加了一些便于存取基本類型的數(shù)據(jù)的方法。
將job加入到scheduler之前,在構(gòu)建JobDetail時(shí),可以將數(shù)據(jù)放入JobDataMap,如下示例:
// define the job and tie it to our DumbJob class JobDetail job = newJob(DumbJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .usingJobData("jobSays", "Hello World!") .usingJobData("myFloatValue", 3.141f) .build();
在job的執(zhí)行過程中,可以從JobDataMap中取出數(shù)據(jù),如下示例:
public class DumbJob implements Job { public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } }
如果你在job類中,為JobDataMap中存儲(chǔ)的數(shù)據(jù)的key增加set方法(如在上面示例中,增加setJobSays(String val)方法),那么Quartz的默認(rèn)JobFactory實(shí)現(xiàn)在job被實(shí)例化的時(shí)候會(huì)自動(dòng)調(diào)用這些set方法,這樣你就不需要在execute()方法中顯式地從map中取數(shù)據(jù)了。
在Job執(zhí)行時(shí),JobExecutionContext中的JobDataMap為我們提供了很多的便利。它是JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的數(shù)據(jù),則后者會(huì)覆蓋前者的值。
下面的示例,在job執(zhí)行時(shí),從JobExecutionContext中獲取合并后的JobDataMap:
public class DumbJob implements Job { public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); ArrayList state = (ArrayList)dataMap.get("myStateData"); state.add(new Date()); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } }
如果你希望使用JobFactory實(shí)現(xiàn)數(shù)據(jù)的自動(dòng)“注入”,則示例代碼為:
public class DumbJob implements Job { String jobSays; float myFloatValue; ArrayList state; public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example state.add(new Date()); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } public void setJobSays(String jobSays) { this.jobSays = jobSays; } public void setMyFloatValue(float myFloatValue) { myFloatValue = myFloatValue; } public void setState(ArrayList state) { state = state; } }
Job實(shí)例
你可以只創(chuàng)建一個(gè)job類,然后創(chuàng)建多個(gè)與該job關(guān)聯(lián)的JobDetail實(shí)例,每一個(gè)實(shí)例都有自己的屬性集和JobDataMap,最后,將所有的實(shí)例都加到scheduler中。
比如,你創(chuàng)建了一個(gè)實(shí)現(xiàn)Job接口的類“SalesReportJob”。該job需要一個(gè)參數(shù)(通過JobdataMap傳入),表示負(fù)責(zé)該銷售報(bào)告的銷售員的名字。因此,你可以創(chuàng)建該job的多個(gè)實(shí)例(JobDetail),比如“SalesReportForJoe”、“SalesReportForMike”,將“joe”和“mike”作為JobDataMap的數(shù)據(jù)傳給對(duì)應(yīng)的job實(shí)例。
當(dāng)一個(gè)trigger被觸發(fā)時(shí),與之關(guān)聯(lián)的JobDetail實(shí)例會(huì)被加載,JobDetail引用的job類通過配置在Scheduler上的JobFactory進(jìn)行初始化。默認(rèn)的JobFactory實(shí)現(xiàn),僅僅是調(diào)用job類的newInstance()方法,然后嘗試調(diào)用JobDataMap中的key的setter方法。你也可以創(chuàng)建自己的JobFactory實(shí)現(xiàn),比如讓你的IOC或DI容器可以創(chuàng)建/初始化job實(shí)例。
在Quartz的描述語言中,我們將保存后的JobDetail稱為“job定義”或者“JobDetail實(shí)例”,將一個(gè)正在執(zhí)行的job稱為“job實(shí)例”或者“job定義的實(shí)例”。當(dāng)我們使用“job”時(shí),一般指代的是job定義,或者JobDetail;當(dāng)我們提到實(shí)現(xiàn)Job接口的類時(shí),通常使用“job類”。
Job狀態(tài)與并發(fā)
關(guān)于job的狀態(tài)數(shù)據(jù)(即JobDataMap)和并發(fā)性,還有一些地方需要注意。在job類上可以加入一些注解,這些注解會(huì)影響job的狀態(tài)和并發(fā)性。
@DisallowConcurrentExecution:將該注解加到j(luò)ob類上,告訴Quartz不要并發(fā)地執(zhí)行同一個(gè)job定義(這里指特定的job類)的多個(gè)實(shí)例。請(qǐng)注意這里的用詞。拿前一小節(jié)的例子來說,如果“SalesReportJob”類上有該注解,則同一時(shí)刻僅允許執(zhí)行一個(gè)“SalesReportForJoe”實(shí)例,但可以并發(fā)地執(zhí)行“SalesReportForMike”類的一個(gè)實(shí)例。所以該限制是針對(duì)JobDetail的,而不是job類的。但是我們認(rèn)為(在設(shè)計(jì)Quartz的時(shí)候)應(yīng)該將該注解放在job類上,因?yàn)閖ob類的改變經(jīng)常會(huì)導(dǎo)致其行為發(fā)生變化。
@PersistJobDataAfterExecution:將該注解加在job類上,告訴Quartz在成功執(zhí)行了job類的execute方法后(沒有發(fā)生任何異常),更新JobDetail中JobDataMap的數(shù)據(jù),使得該job(即JobDetail)在下一次執(zhí)行的時(shí)候,JobDataMap中是更新后的數(shù)據(jù),而不是更新前的舊數(shù)據(jù)。和 @DisallowConcurrentExecution注解一樣,盡管注解是加在job類上的,但其限制作用是針對(duì)job實(shí)例的,而不是job類的。由job類來承載注解,是因?yàn)閖ob類的內(nèi)容經(jīng)常會(huì)影響其行為狀態(tài)(比如,job類的execute方法需要顯式地“理解”其”狀態(tài)“)。
如果你使用了@PersistJobDataAfterExecution注解,我們強(qiáng)烈建議你同時(shí)使用@DisallowConcurrentExecution注解,因?yàn)楫?dāng)同一個(gè)job(JobDetail)的兩個(gè)實(shí)例被并發(fā)執(zhí)行時(shí),由于競(jìng)爭(zhēng),JobDataMap中存儲(chǔ)的數(shù)據(jù)很可能是不確定的。
到此這篇關(guān)于Quartz中的Job與JobDetail解析的文章就介紹到這了,更多相關(guān)Job與JobDetail解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java使用jdbc連接數(shù)據(jù)庫(kù)工具類和jdbc連接mysql數(shù)據(jù)示例
這篇文章主要介紹了java使用jdbc連接數(shù)據(jù)庫(kù)的工具類和使用jdbc連接mysql數(shù)據(jù)的示例,需要的朋友可以參考下2014-03-03通過Spring AOP實(shí)現(xiàn)異常捕捉機(jī)制
在開發(fā)過程中,異常處理是一個(gè)不可忽視的重要環(huán)節(jié),合理、優(yōu)雅地處理異常不僅能提高代碼的魯棒性,還能提升系統(tǒng)的用戶體驗(yàn),本文將介紹如何通過Spring AOP實(shí)現(xiàn)一個(gè)高效的異常捕捉機(jī)制,使得異常處理變得更加優(yōu)雅和統(tǒng)一,需要的朋友可以參考下2024-08-08Java項(xiàng)目Guava包?HashMultimap使用及注意事項(xiàng)
guava基本上可以說是java開發(fā)項(xiàng)目中,大概率會(huì)引入的包,今天介紹的主角是一個(gè)特殊的容器HashMultmap,可以簡(jiǎn)單的將它的數(shù)據(jù)結(jié)構(gòu)理解為Map<K,?Set<V>>,今天主要介紹下基礎(chǔ)的知識(shí)點(diǎn)?HashMultmap級(jí)使用,感興趣的朋友一起看看吧2022-05-05SpringMVC 域?qū)ο蠊蚕頂?shù)據(jù)的實(shí)現(xiàn)示例
本文主要介紹了SpringMVC 域?qū)ο蠊蚕頂?shù)據(jù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Javaweb使用thymeleaf局部刷新結(jié)合Layui插件實(shí)現(xiàn)Html分頁(yè)
本文主要介紹了Javaweb使用thymeleaf局部刷新結(jié)合Layui插件實(shí)現(xiàn)Html分頁(yè),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Spring使用@Async出現(xiàn)循環(huán)依賴原因及解決方案分析
在Spring框架中,啟用異步功能需要在應(yīng)用主類上添加@EnableAsync注解,當(dāng)項(xiàng)目中存在循環(huán)引用時(shí),如一個(gè)異步類MessageService和一個(gè)常規(guī)類TaskService相互引用,并且這兩個(gè)類位于同一包內(nèi),這種情況下可能會(huì)觸發(fā)Spring的循環(huán)依賴異常2024-10-10