Quartz-Job 详解
文章目錄
- 概述
- JobDataMap
- Job 實例
- Job 狀態和并發
- Job 的其它屬性
- JobExecutionException
概述
Quartz- Quartz API以及Jobs 和Triggers介紹 中 ,我們可以看到 Job是相當容易實現,只需要實現Job接口,重寫execute方法即可.
Quartz 中可能需要為 Job 實例設置屬性,這個功能通過 JobDetail 類來完成。
JobDetail 實例通過 JobBuilder 創建。你可以使用靜態導入所有的方法,這樣可以在你的代碼中使用 DSL 風格:
import static org.quartz.JobBuilder.*; // define the job and tie it to our HelloJob classJobDetail job = newJob(HelloJob.class).withIdentity("myJob", "group1") // name "myJob", group "group1".build();// Trigger the job to run now, and then every 40 secondsTrigger trigger = newTrigger().withIdentity("myTrigger", "group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(40).repeatForever()) .build();// Tell quartz to schedule the job using our triggersched.scheduleJob(job, trigger);現在,假設 HelloJob 的定義如下:
public class HelloJob implements Job {public HelloJob() {}public void execute(JobExecutionContext context)throws JobExecutionException{System.err.println("Hello! HelloJob is executing.");}}注意,我們給了調度器一個 JobDetail 實例,JobDetail 中提供了 Job 的 class 對象,因此它知道調用的 Job 類型。每次調度器執行 Job,它會在調用 execute(…) 方法前創建一個新的 Job 實例。當執行完成后,所有 Job 的引用將會丟棄,這些對象會被垃圾回收。
基于前面的描述,首先 Job 類需要一個無參構造方法,另外,在 Job 中存儲狀態屬性是沒有意義的,因為每次執行完成后,對象將會被刪除。
JobDataMap
JobDataMap 可以用來保存數據對象(序列化)。JobDataMap 其實是 Java Map 接口的一個實現,并且添加了一些方便的方法用于存儲和獲取原始數據類型。
下面的例子將存儲數據到 JobDataMap :
// define the job and tie it to our DumbJob classJobDetail job = newJob(DumbJob.class).withIdentity("myJob", "group1") // name "myJob", group "group1".usingJobData("jobSays", "Hello World!").usingJobData("myFloatValue", 3.141f).build();下面的例子演示如何在執行期間從 JobDataMap 獲取數據:
package com.xgj.quartz.quartzItself.jobDataMap;import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger;import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory;public class MyJob2Runner {public static void main(String[] args) {try {// Grab the Scheduler instance from the FactorySchedulerFactory factory = new StdSchedulerFactory();Scheduler scheduler = factory.getScheduler();// startscheduler.start();// define the job and tie it to our MyJob classJobDetail job = newJob(MyJob2.class).withIdentity("myJob", "group1").usingJobData("jobSays", "Hello World!").usingJobData("myFloatValue", 3.141f).build();// Trigger the job to run now, and then every 40 secondsTrigger trigger = newTrigger().withIdentity("myTrigger", "group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(40).repeatForever()).build();// Tell quartz to schedule the job using our triggerscheduler.scheduleJob(job, trigger);} catch (SchedulerException e) {e.printStackTrace();}} } package com.xgj.quartz.quartzItself.jobDataMap;import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey;public class MyJob2 implements Job {public MyJob2() {}@Overridepublic 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 MyJob2 says: " + jobSays+ ", and val is: " + myFloatValue);}}運行結果:
INFO StdSchedulerFactory - Using default implementation for ThreadExecutor INFO SimpleThreadPool - Job execution threads will use class loader of thread: main INFO SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl INFO QuartzScheduler - Quartz Scheduler v.2.2.3 created. INFO RAMJobStore - RAMJobStore initialized. INFO QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.3) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.NOT STARTED.Currently in standby mode.Number of jobs executed: 0Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.INFO StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties' INFO StdSchedulerFactory - Quartz scheduler version: 2.2.3 INFO QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started. Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141 Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141........... ...........如果你使用 JobStore 存儲,那么你需要小心決定在 JobDataMap 中存放什么數據,因為對象將會序列化,因此會有一些 class 類型的問題。標準的 Java 類都非常安全,但是如果你要使用自己定義的類,那么任何時候你要改變類定義,都要小心不要破壞兼容性。你可以只保存 String 和原始數據類型從而消除可能發生的序列化問題。
如果你添加了 set 方法到你的 Job 類中,并且和 JobDataMap 中存放的鍵一致(例如,上面例子中添加 setJobSays(String val) 方法),然后 Quartz 默認的 JobFactory 實現將會自動在 Job 實例化的時候調用這些 set 方法。
Trigger 也可以關聯 JobDataMap。這可用于當你需要在多個 Trigger 中使用相同的 Job 的時候,為每個 Job 設置不同的輸入數據。JobDataMap 可以在 Job 執行期間從 JobExecutionContext 中獲得。它將會合并 JobDetail 和 Trigger 中的 JobDataMap,如果名稱相同,那么后者的值將會覆蓋前者的值。
下面的例子將演示如何從 JobExecutionContext 中獲取 JobDataMap:
public class MyJob implements Job {public MyJob() {}public void execute(JobExecutionContext context)throws JobExecutionException{JobKey key = context.getJobDetail().getKey();JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous exampleString jobSays = dataMap.getString("jobSays");float myFloatValue = dataMap.getFloat("myFloatValue");System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);}}或者,如果你想要依賴 JobFactory 注入映射值到你的類中,那么可以使用下面的代碼:
package com.xgj.quartz.quartzItself.jobDataMap;import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey;public class MyJob implements Job {private String jobSays;private float myFloatValue;public MyJob21() {}@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {JobKey key = context.getJobDetail().getKey();// Note the difference from the previousJobDataMap dataMap = context.getMergedJobDataMap();System.err.println("Instance " + key + " of MyJob2 says: " + jobSays+ ", and val is: " + myFloatValue);}public void setJobSays(String jobSays) {this.jobSays = jobSays;}public void setMyFloatValue(float myFloatValue) {this.myFloatValue = myFloatValue;}}日志輸出同上。
Job 實例
你可以創建一個 Job 類,然后通過創建多個 JobDetail 實例與 Job 關聯,并保存到調度器中(每個任務都有自己的屬性和 JobDataMap),這個 JobDetail 稱為 Job 實例。
例如,你可以創建一個實現了 Job 接口的類,命名為“SalesReportJob”。這個類可以接收一個參數(通過 JobDataMap)用于定義銷售報表基于哪個銷售人員。它們可以創建多個 Job 實例(使用 JobDetail),例如 “SalesReportForJoe” 和 “SalesReportForMike”,這里使用了由 JobDataMap 傳入 “joe” 和 “mike” 作為參數。
當 Trigger 被觸發,關聯的 JobDetail 將會被加載,并且 Job 類會通過 JobFactory 配置到 Scheduler。默認的 JobFactory 將會簡單地調用 Job Class 的 newInstance() 方法,并嘗試調用 set 方法將 JobDataMap 中同名的屬性設置到 Job 中。
Job 狀態和并發
有一組可添加到 Job 的 Annotation,可以影響 Quartz 的行為。
-
@DisallowConcurrentExecution 添加到 Job 類后,Quartz 將不會同時執行多個 Job 實例。我們用上一節的例子來講解,如果 “SalesReportJob” 上添加了這個Annotation,那么同時只能執行一個“SalesReportForJoe”,但是卻可以同時執行“SalesReportForMike”。因此,可以說這個約束是基于JobDetail 的而不是基于 Job 的。
-
@PersistJobDataAfterExecution 添加到 Job 類后,表示 Quartz 將會在成功執行 execute()方法后(沒有拋出異常)更新 JobDetail 的JobDataMap,下一次執行相同的任務(JobDetail)將會得到更新后的值,而不是原始的值。就像@DisallowConcurrentExecution 一樣,這個注釋基于 JobDetail 而不是 Job 類的實例。
如果你使用了 @PersistJobDataAfterExecution 注釋,那么強烈建議你使用 @DisallowConcurrentExecution 注釋,這是為了避免出現并發問題,當多個 Job 實例同時執行的時候,到底使用了哪個數據將變得很混亂。
Job 的其它屬性
下面列舉了一些通過 JobDetail 定義的 Job 屬性:
-
Durability – 持久性,如果 Job 是非持久性的,那么執行完 Job 后,如果沒有任何活動的 Trigger與之關聯,那么將會被調度器自動刪除。換句話說,非持久性的 Job 的生命周期與它關聯的 Trigger 相關。
-
RequestsRecovery – 如果任務設置了 RequestsRecovery,那么它在調度器發生硬停止(例如,當前進程crash,或者機器宕機)后,當調度器再次啟動的時候將會重新執行。這種情況下,JobExecutionContext.isRecovering()方法將會返回 true。
JobExecutionException
最后,我們來看看 Job.execute(…) 方法。這個方法只允許拋出一種異常(包括 RuntimeException),那就是 JobExecutionException。正是因為如此,你通常需要將 execute() 方法中的所有內容放入 try-catch 語句塊中。你也需要花點時間看看 JobExecutionException 的文檔,你的任務可以使用它提供的各種指令來控制如何處理異常。
總結
以上是生活随笔為你收集整理的Quartz-Job 详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Quartz- Quartz API以及
- 下一篇: Quartz-Trigger详解