關於Quartz的一些概念介紹

Job 非常容易實現,這個接口中只有一個 execute() 方法。Quartz 中可能需要爲 Job 實例設置屬性,這個功能通過 JobDetail 類來完成。

JobDetail 實例通過 JobBuilder 創建。你可以使用靜態導入所有的方法,這樣可以在你的代碼中使用 DSL 風格:

import static org.quartz.JobBuilder.*;

讓我們花點時間來討論 Job 在 Quartz 中的生命週期:

// 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);

現在,假設 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 中存儲狀態屬性是沒有意義的,因爲每次執行完成後,對象將會被刪除。

你可能想問“我如何爲 Job 實例提供配置屬性”或者“我怎麼保存 Job 的狀態數據”。這些問題的關鍵是 JobDataMap 對象,它是 JobDetail 對象中的一部分。

JobDataMap

JobDataMap 可以用來保存數據對象(序列化)。JobDataMap 其實是 Java Map 接口的一個實現,並且添加了一些方便的方法用於存儲和獲取原始數據類型。

下面的例子將存儲數據到 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();

下面的例子演示如何在執行期間從 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 中存放什麼數據,因爲對象將會序列化,因此會有一些 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 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 注入映射值到你的類中,那麼可以使用下面的代碼:

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() 方法更加清晰整潔。無論使用哪種方式,都取決於你自己的決定。

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 實例(什麼是 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 的文檔,你的任務可以使用它提供的各種指令來控制如何處理異常。

發佈了40 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章