Quartz官方教程翻譯系列-Lesson 3

原文地址: http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/tutorials/tutorial-lesson-03.htmlhtml

第三課: Jobs 與 Job Details 更多相關編程

正如你在第二課所見,任務實現起來至關簡單,僅僅有一個 'execute' 方法在接口。這裏僅有少數東西你須要明白關於jobs的本質,關於Job 接口 execute(...) 方法,還有 JobDetails。安全

當你實現一個任務類,有知道如何作特定類型工做的代碼,Quartz 須要瞭解關於各類你但願任務實例擁有的屬性。這個就經過JobDetail完成,在上一節已經簡單提過。併發

JobDetail 實例是經過 JobBuilder 類構建的。你一般能夠用一個靜態導入所有它的方法,爲了讓你的代碼看起來有領域規範語言的感受。less

import static org.quartz.JobBuilder.*;複製代碼

讓咱們花一點時間討論一點 Jobs的本性和 job實例的在Quartz的生命週期。首先讓咱們回頭看一眼咱們在第一課的代碼片斷:ui

// 定義任務並綁定咱們的 HelloJob 類
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .build();

  // 觸發任務馬上運行,而且妹40秒有也行
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())            
      .build();

  // 告訴 quartz 執行調度使用的觸發器
  sched.scheduleJob(job, trigger);複製代碼

如今考慮任務類 "HelloJob" 定義以下:this

public class HelloJob implements Job {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println("Hello!  HelloJob is executing.");
    }
  }複製代碼

注意咱們爲任務調度指定一個JobDetail實例,而且在構建JobDetail時,只需提供做業的類便可執行的做業類型。每一次任務調度執行任務,它會在被調用 execute(...)方法前被建立新的實例。當執行結束,任務類實例的引用被丟棄,而且實例會被回收。這種行爲的一個後果就是任務必要有一個無參構造器(當使用默認的 JobFactoy 實現時)。另外一個結果就是在做業類上定義狀態數據字段是沒有意義的-他們的值不會在任務執行期間被保存。編碼

你可能會想問「我怎樣才能爲一個任務實例保存屬性/配置?」 還有 「我怎樣才能跟蹤一個任務的狀態在執行之間?」他們的答案都是同樣的,關鍵是JobDataMap, JobDetail對象的一部分。spa

JobDataMap

JobDataMap 能夠用來持有任意數量(序列化)數據對象那些你在任務實例執時可用的。JobDataMap 是Java Map接口的一個實現,而且加了一些方便的方法用於存儲和恢復原始類型的數據。設計

這裏是關於在定義/構建 JobDetail 存放數據到 JobDataMap的代碼片斷,在一個任務加入調度任務以前:

// 定義任務而且綁定到DumbJob類
  JobDetail job = newJob(DumbJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .usingJobData("jobSays", "Hello World!")
      .usingJobData("myFloatValue", 3.141f)
      .build();複製代碼

如下是一個在任務執行見從JobDataMap 獲取數據的簡單例子:

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);
    }
  }複製代碼

若是你想要一個持久化的 任務存儲(在本教程的 JobStore章節討論過) 你應該謹慎決定放在 JobDataMap的地方,由於對象會被序列化,所以,它們很容易出現類版本控制問題。顯然,標準的Java類型應該是很安全的,但超過這個,任什麼時候候某人改變一個你已經實現的類實例的定義,必須當心不要破壞兼容性。另外一個選擇是,你能夠放 JDBC_JobStore 和 JobDataMap 進入只容許原語和字符串存儲在映射中的模式,從而消除了之後出現序列化問題的可能性。

若是你增長對應於 JobDataMap中鍵名的 setter 方法到你的任務類(好像下面例子的 data 中 setJobSay(String val)) 方法,而後Qurartz 的默認 JobFactory 實例會自動調用他的 setter 在 Job 實例化後,所以,無需在你的execute方法內從映射中顯式獲取值。

觸發器一樣能夠 JodDataMaps關聯。這樣在當你有一個任務被存儲在調度程序中給多個觸發器按期/重複使用的狀況下頗有用,然而,對於每一個獨立的觸發,你都爲任務提供不一樣的數據輸入。

JobDataMap 在任務執行的時候,很方便在JobExeccutionContext找到。它是JobDetail上的JobDataMap和Trigger上的JobDataMap的結合,使用後者中的值覆蓋前者中的任何同名值。

這裏是一個簡短的例子,演示怎樣從 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 注入 數據Map值經過你本身類,它看起來會像這樣:

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;
    }

  }複製代碼

你會注意到整體代碼更長了,單在 execute() 方法裏面的代碼更清晰。有人可能會說,雖然代碼更長,單實際用到了更少的代碼,若是編程的 IDE 有本身建立 setter 方法,而沒必要手動編寫各個調用的代碼來從JobDataMap檢索值。由你選擇。

任務"實例"

不少用戶花時間搞不清什麼是任務實例。咱們嘗試在如下的章節裏面說清楚任務狀態和併發性。

你能夠建立一個單獨的任務類,而且存儲多個「實例定義」經過建立JobDetails的多個實例在調度程序中-每一個都有其本身的屬性和JobDataMap-並將它們所有添加到調度程序中。

舉例,你能夠建立一個名字叫 "SalesReportJob"的任務實例。這個任務可能被指望的參數發送(好像 JobDataMap) 到指定銷售報告所依據的銷售人員的姓名。他們可能在任務建立多個定義(JobDetails) ,好像"SalesReportForJbe" 和 "SaleReportForMike" 用"joe" 和 "mike" 在相應的JobDataMaps中指定爲各個做業的輸入。

當一個觸發器點燃,JobDetail(實例定義) 關聯被加載,而且 任務類實例被 JobFactory 引用 配置到 執行調度。默認 JobFactory 調用 job 類的 newInstance() 方法,意圖調用在類上與JobDataMap的鍵名匹配的方法。你可能想建立你本身的 JobFactory 實例去完成你應用的 IOC 或者 DI 容器 生產/初始化任務實例。

在 "Quartz speak" , 咱們說起到每一個存儲的JobDetail 好像 "job definiton" 或 "JobDetail instance" ,而後咱們說起到每個執行的任務好似"job instance" 或者 "instance of a job definition"。一般,若是咱們僅僅使用 「job」這個詞,咱們是說起一個命名定義,或者 JobDetail。當咱們說起到 job 類實例,咱們一般用術語 "job class"。

任務狀態和並行

如今,一些關於任務狀態數據(也就是 JobDataMap)和並行)的附加說明。這裏是有一對註解能夠在加在你的任務類上,影響 Quzrtz 的行爲。

@DisallowConcurrentExecution 是一個能夠加在任務類上的註解,告訴 Quartz不能夠在指定的任務定義(這裏指指定的任務類)並行執行多個實例。注意這裏的用詞,是很是謹慎選擇的。在上一節的例子,若是 "SalesReportJob" 有這個註解,那麼僅有一個 "SalesReportForJoe"的實例能夠在指定時間執行,但能夠並行執行一個"SaleReportForMike"的實例。約束是基於實例定義(JobDetail),不是在任務的實例。然而,它是取決於(在Quartz設計期間)註解是否對自己進行了處理,由於它常常對類的編碼方式產生影響。

@PersistJobDataAfterExecution 是一個能夠加在任務類上的註解,告訴Quzrtz 在執行 execute() 方法成功後(沒有拋出異常)更新JobDetail的 JobDataMap存儲,這樣下一次執行一樣的任務(JobDetail) 接收到更新值而不是存儲的值好像@DisallowConcurrentExcetion 同樣,適用於一個任務的實例,不是一個任務類是實例,取決於任務類的屬性由於它一般對類的編碼方式產生影響。(好像"statefulness" 會須要明確的"understood"經過在execute 方法裏的代碼)。

若是你用@PersistJobDataAfterExecution 這個註解,你須要慎重考慮同時用@DisallowConcurrentExecution 註解,爲了不一個相同的任務(JobDetail)並行時存儲的數據致使可能的混亂(速度混亂)。

其餘的任務屬性

這裏是一個快速總結定義一個任務實例即 JobDetail 對象的其餘屬性:

  • 持久性 - 若是一個任務是非持久的,他會從任務調度執行一旦沒有任何的觸發器關聯,非持久任務的生命週期綁定在觸發器的存在。
  • 可恢復性- 若是一個任務「可恢復」,並它執行的時候被「硬關閉」(也就是進程在跑的過程當中奔潰,或者機器關機),那麼當任務調度再次開始時,會從新執行。在這種狀況下, JobExecutionContext.isRecovering() 方法返回 true。

JobExecutionException

最後,咱們須要告知你 Job.execute(...) 的一些細節。惟一的一種異常類型(包含RuntimeEcxeption) 你能夠從執行方法中拋出的異常是 JobExecutionException。 正是由於這樣,你應該一般包裝所有內容在「try-catch」塊中。你還應該花點時間閱讀文檔中,關於JobExecutionExcetion的部分,爲調度程序提供各類指令,以肯定您但願如何處理異常。

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索