Job與JobDetail在Quartz中的區(qū)別有哪些?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
成都創(chuàng)新互聯(lián)公司主要從事網(wǎng)站制作、網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)浦口,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
Quartz可以用來(lái)做什么?
Quartz是一個(gè)任務(wù)調(diào)度框架。比如你遇到這樣的問題
想每月25號(hào),信用卡自動(dòng)還款
想每年4月1日自己給當(dāng)年暗戀女神發(fā)一封匿名賀卡
想每隔1小時(shí),備份一下自己的愛情動(dòng)作片 學(xué)習(xí)筆記到云盤
這些問題總結(jié)起來(lái)就是:在某一個(gè)有規(guī)律的時(shí)間點(diǎn)干某件事。并且時(shí)間的觸發(fā)的條件可以非常復(fù)雜(比如每月最后一個(gè)工作日的17:50),復(fù)雜到需要一個(gè)專門的框架來(lái)干這個(gè)事。 Quartz就是來(lái)干這樣的事,你給它一個(gè)觸發(fā)條件的定義,它負(fù)責(zé)到了時(shí)間點(diǎn),觸發(fā)相應(yīng)的Job起來(lái)干活。
廢話不多說(shuō),代碼杠杠的。。。
public static void main(String[] args) { try { //創(chuàng)建scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //定義一個(gè)Trigger Trigger trigger =TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") //定義name/group .startNow()//一旦加入scheduler,立即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger .withIntervalInSeconds(1) //每隔一秒執(zhí)行一次 .repeatForever()) //一直執(zhí)行 .build(); //定義一個(gè)JobDetail JobDetail job =JobBuilder.newJob(HelloQuartz.class) //定義Job類為HelloQuartz類,這是真正的執(zhí)行邏輯所在 .withIdentity("job1", "group1") //定義name/group .usingJobData("name", "quartz") //定義屬性 .build(); //加入這個(gè)調(diào)度 scheduler.scheduleJob(job, trigger); //啟動(dòng)之 scheduler.start(); //運(yùn)行一段時(shí)間后關(guān)閉 Thread.sleep(10000); scheduler.shutdown(true); } catch (Exception e) { e.printStackTrace(); } }
HelloQuartz類
public class HelloQuartz implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); String name = detail.getJobDataMap().getString("name"); System.out.println("say hello to " + name + " at " + new Date()); } }
jar包:
這個(gè)例子很好的覆蓋了Quartz最重要的3個(gè)基本要素:
Scheduler:調(diào)度器。所有的調(diào)度都是由它控制。
Trigger: 定義觸發(fā)的條件。例子中,它的類型是SimpleTrigger,每隔1秒中執(zhí)行一次(什么是SimpleTrigger下面會(huì)有詳述)。
JobDetail & Job: JobDetail 定義的是任務(wù)數(shù)據(jù),而真正的執(zhí)行邏輯是在Job中,例子中是HelloQuartz。 為什么設(shè)計(jì)成JobDetail + Job,不直接使用Job?這是因?yàn)槿蝿?wù)是有可能并發(fā)執(zhí)行,如果Scheduler直接使用Job,就會(huì)存在對(duì)同一個(gè)Job實(shí)例并發(fā)訪問的問題。而JobDetail & Job 方式,sheduler每次執(zhí)行,都會(huì)根據(jù)JobDetail創(chuàng)建一個(gè)新的Job實(shí)例,這樣就可以規(guī)避并發(fā)訪問的問題。
Scheduler
Scheduler就是Quartz的大腦,所有任務(wù)都是由它來(lái)設(shè)施。
Schduelr包含一個(gè)兩個(gè)重要組件: JobStore和ThreadPool。
JobStore是會(huì)來(lái)存儲(chǔ)運(yùn)行時(shí)信息的,包括Trigger,Schduler,JobDetail,業(yè)務(wù)鎖等。它有多種實(shí)現(xiàn)RAMJob(內(nèi)存實(shí)現(xiàn)),JobStoreTX(JDBC,事務(wù)由Quartz管理),JobStoreCMT(JDBC,使用容器事務(wù)),ClusteredJobStore(集群實(shí)現(xiàn))、TerracottaJobStore(什么是Terractta)。
ThreadPool就是線程池,Quartz有自己的線程池實(shí)現(xiàn)。所有任務(wù)的都會(huì)由線程池執(zhí)行。
SchedulerFactory
SchdulerFactory,顧名思義就是來(lái)用創(chuàng)建Schduler了,有兩個(gè)實(shí)現(xiàn):DirectSchedulerFactory和 StdSchdulerFactory。前者可以用來(lái)在代碼里定制你自己的Schduler參數(shù)。后者是直接讀取classpath下的quartz.properties(不存在就都使用默認(rèn)值)配置來(lái)實(shí)例化Schduler。通常來(lái)講,我們使用StdSchdulerFactory也就足夠了。
SchdulerFactory本身是支持創(chuàng)建RMI stub的,可以用來(lái)管理遠(yuǎn)程的Scheduler,功能與本地一樣,可以遠(yuǎn)程提交個(gè)Job什么的。
1.job
實(shí)現(xiàn)類JobDetail
JobDetail job = JobBuilder.newJob(RemindJob.class) .withIdentity("job1", "group1").build();//創(chuàng)建一個(gè)任務(wù) /** * 創(chuàng)建觸發(fā)器 * 第一種方式 不太好 */ SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "myTriggerGroup"). withSchedule(SimpleScheduleBuilder.simpleSchedule(). withIntervalInSeconds(3). repeatForever()). startAt(new Date(System.currentTimeMillis()+1000)).build(); /** * 創(chuàng)建觸發(fā)器 * 第二種 方式 非常好 * 可以 好用 2013年每月的第三個(gè)星期五上午10:30觸發(fā) 0 30 10 ? * 6#3 2013 * 2016年每月的第一個(gè)星期四下午16:17觸發(fā) 0 17 16 ? * 5#1 2016 * 每天15點(diǎn)到16點(diǎn)每5分鐘運(yùn)行一次,此外,每天17點(diǎn)到18點(diǎn)每5分鐘運(yùn)行一次 */ /*CronTrigger trigger=TriggerBuilder.newTrigger() .withIdentity("myTrigger", "myTriggerGroup") .withSchedule(CronScheduleBuilder.cronSchedule("0 18 16 ? * 5#1 2016")).build();*/ SchedulerFactory sf=new StdSchedulerFactory();//創(chuàng)建調(diào)度者工廠 Scheduler scheduler = sf.getScheduler();//創(chuàng)建一個(gè)調(diào)度者 scheduler.scheduleJob(job,trigger);//注冊(cè)并進(jìn)行調(diào)度 scheduler.start();//啟動(dòng)調(diào)度 //Thread.sleep(millis) //scheduler.shutdown();//關(guān)閉調(diào)度
RemindJob 類的定義
*/ public class RemindJob implements Job { private RemindService service=new RemindService(); @Override public void execute(JobExecutionContext context) throws JobExecutionException { service.printPlan("你好!"); Date date=new Date(); String time = date.toString(); System.out.println(time+"job is starting"); }
可以看到,我們傳給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í)行策略帶來(lái)的一個(gè)后果是,job必須有一個(gè)無(wú)參的構(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,如下示例:
JobDetail job=JobBuilder.newJob(RemindJob.class) .withIdentity("job1", "group1") .usingJobData("hello", "we are family") .build();
在job的執(zhí)行過程中,可以從JobDataMap中取出數(shù)據(jù),如下示例:
@Override public void execute(JobExecutionContext context) throws JobExecutionException { service.printPlan("你好!"); JobKey key=context.getJobDetail().getKey(); JobDataMap map = context.getJobDetail().getJobDataMap(); String string = map.getString("hello"); System.out.println(key+"==========="+string); Date date=new Date(); String time = date.toString(); System.out.println(time+"job is starting"); }
如果你使用的是持久化的存儲(chǔ)機(jī)制(本教程的JobStore部分會(huì)講到),在決定JobDataMap中存放什么數(shù)據(jù)的時(shí)候需要小心,因?yàn)镴obDataMap中存儲(chǔ)的對(duì)象都會(huì)被序列化,因此很可能會(huì)導(dǎo)致類的版本不一致的問題;Java的標(biāo)準(zhǔn)類型都很安全,如果你已經(jīng)有了一個(gè)類的序列化后的實(shí)例,某個(gè)時(shí)候,別人修改了該類的定義,此時(shí)你需要確保對(duì)類的修改沒有破壞兼容性;更多細(xì)節(jié),參考現(xiàn)實(shí)中的序列化問題。另外,你也可以配置JDBC-JobStore和JobDataMap,使得map中僅允許存儲(chǔ)基本類型和String類型的數(shù)據(jù),這樣可以避免后續(xù)的序列化問題。
如果你在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:
@Override public void execute(JobExecutionContext context) throws JobExecutionException { service.printPlan("你好!"); JobKey key=context.getJobDetail().getKey(); /* JobDataMap map = context.getJobDetail().getJobDataMap(); String string = map.getString("hello"); System.out.println(key+"==========="+string);*/ JobDataMap map = context.getMergedJobDataMap(); String string = map.getString("hello"); System.out.println(key+"--------------------- "+string);
看完上述內(nèi)容,你們掌握J(rèn)ob與JobDetail在Quartz中的區(qū)別有哪些的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!