Quartz Scheduler 開發指南(1)

Quartz Scheduler 開發指南(1)

原文地址:http://www.quartz-scheduler.org/generated/2.2.2/html/qtz-all/html

實例化調度程序(Instantiating the Scheduler)

在使用Scheduler調度程序前,它須要被實例化。你可使用SchedulerFactory實現java

一些Quartz用戶經過使用JNDI中的Factory實例,還有一些直接使用Factory實例進行實例化(以下方的例子)安全

一旦Scheduler調度程序被實例化後, 它能夠啓動,保持準備狀態,關閉。注意:一旦關閉了Scheduler,再也不次實例化是不能重啓的。Trigger不能被觸發,直到Scheduler啓動。Trigger一樣不會觸發,當Scheduler處於暫停狀態併發

下方例子:實例化,啓動一個Scheduler,並調度一個任務(Job)執行函數

SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); 
Scheduler sched = schedFact.getScheduler(); 
sched.start(); 
// define the job and tie it to our HelloJob class 
JobDetail job = newJob(HelloJob.class) 
  .withIdentity("myJob", "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.javaui

public class HelloJob implements Job {
    public static final Logger _log = LoggerFactory.getLogger(HelloJob.class);
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        _log.info("Hello World! - " + new Date() + "-" + jobExecutionContext.getJobDetail().getJobDataMap().get("key1"));
    }
}

關鍵接口(Key Interfaces)

Quartz API中的關鍵接口this

  • Scheduler - 與調度程序交互的主要接口
  • Job - 調度程序調度執行的任務須要實現這個接口
  • JobDetail - 定義job實例
  • Trigger - 調度程序執行哪一個任務的觸發條件
  • JobBuilder - 用來定義/建立JobBuilder實例(指定任務實例)
  • TriggerBuilder - 用來定義/建立 Trigger實例

一個調度程序(Scheduler)的生命週期受到他的建立(經過SchedulerFactory建立)和關閉方法(shutdown() Method)控制線程

一個建立好的調度程序能夠用來添加(add)、移除(remove)、列舉(list) 任務(jobs)和觸發器(triggers),還能夠執行其餘調度相關的操做(例如暫停觸發器)。可是調度程序(Scheduler)實際上不會進行任何觸發(執行指定任務),直到它被開啓(經過start()方法),就如上面的那個例子設計

實現上方的例子須要導入以下:code

import static org.quartz.JobBuilder.*; 
import static org.quartz.SimpleScheduleBuilder.*; 
import static org.quartz.CronScheduleBuilder.*; 
import static org.quartz.CalendarIntervalScheduleBuilder.*; 
import static org.quartz.TriggerBuilder.*; 
import static org.quartz.DateBuilder.*;

Jobs and Triggers

任務是一個類,須要實現Job接口。以下方展現的,這個接口只有一個方法

package org.quartz;  
public interface Job {  
public void execute(JobExecutionContext context)  
  throws JobExecutionException;  
}

當一個任務的觸發器觸發時,調度程序的一個工做線程將調用Excute()方法。傳遞給該方法的參數JobExecutionContext 對象提供了任務實例以及該實例運行環境的信息,包括一個執行調度程序的調度處理、一個觸發執行的觸發器處理、任務的JobDetail對象,以及一些其餘信息

在一個任務添加到調度程序中時,JobDetail對象被Quartz建立。這個對象包含不少對Job的屬性設定和一個JobDataMap,JobDataMap能夠儲存給定類實例的狀態信息。JobDetail 對象其實是對Job實例的定義

Trigger對象用來觸發任務的執行。當你但願調度一個Job時,你實例化一個觸發器並調整它的特性來提供你想要的調度方式

Triggers也許也有一個JobDataMap和它綁定。JobDataMap傳遞一些特別的觸發觸發器的任務參數時特別有用。Quartz擁有不少不一樣類型的觸發器,但最經常使用的類型是SimpleTrigger 和 CronTrigger

  • SimpleTrigger十分方便---------你須要單次執行(在一個給定是時間單一的執行),或者你須要在一個給定的時間觸發他,每隔一段時間觸發一次,或者說觸發N次
  • CronTrigger十分方便 ---------- 基於日曆觸發,例如每星期五中午或每月的第十天的10:15。

爲何同時擁有Job和Trigger?有的任務調度程序沒有區分Job和Trigger的概念。一些定義Job是一個簡單的執行時間帶有一些小的標識,另外一些則很像Quartz中Job和Trigger對象的結合。Quartz的設計建立了一個分離將時間進度表和根據該進度表執行的任務分離。這個設計有不少好處。

例如,你能夠建立不少任務並把它們放在任務時間表中,獨立於觸發器。這容許你將相同的任務綁定在不一樣的觸發器中。這種鬆耦合的另外一個好處是在綁定的觸發器過時後依舊在調度時間表中的任務能夠再次被配置。這將容許你晚點調度它們,而不用再次定義它們。這也容許你修改或者代替觸發器(Trigger)而不用從新定義綁定的任務(Job)

Jobs and JobDetails

Job接口實現很簡單,只有一個excute()方法。但依舊有一些關於Job的特色,excute()方法,JobDetails須要你瞭解

當你實現的一個Job類在實際工做中須要執行一些特定類型的任務, Quarzt須要被告知Job實例擁有的不少屬性,這個能夠經過JobDetail實現。

JobDetail實例經過JobBuilder類建立,一般靜態導入他的全部方法

import static org.quartz.JobBuilder.*;

下方的例子定義了一個Job類並執行。

SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); 
Scheduler sched = schedFact.getScheduler(); 
sched.start(); 
// define the job and tie it to our HelloJob class 
JobDetail job = newJob(HelloJob.class) 
  .withIdentity("myJob", "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.java

public class HelloJob implements Job {
    public static final Logger _log = LoggerFactory.getLogger(HelloJob.class);
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        _log.info("Hello World! - " + new Date() + "-" + jobExecutionContext.getJobDetail().getJobDataMap().get("key1"));
    }
}

注意:咱們給了Scheduler一個JobDetail實例,它知道將被執行的Job的類型,只要簡單的提供Job的類當咱們建立JobDetail時。每次Scheduler執行這個Job時,在調用Excute()前它將建立這個類的實例。當執行完成時,對Job類實例的引用將被丟棄,而且該實例會被垃圾收集(即丟棄)。

這一行爲的後果是Jobs必須有一個無參的構造函數(使用默認的JobFactory實現)。另外一個後果是Job類中沒有定義數據,這些值是無用的,在Job執行時這些值不會被保存的

給job實例提供配置信息或者在Job執行時跟蹤Job狀態,可使用JobDataMap,即JobDetail對象的一部分。

JobDataMap

JobDataMap能夠用來保存任何數量的(序列化)數據對象,這些數據能夠在任務實例執行時得到。JobDataMap是Java Map接口的實現,同時還添加了一些方便的方法,用於存儲和檢索原始數據類型。

下面是在定義建立JobDetail時將數據存入JobDataMap,這個優先於將Job添加到Scheduler中

// 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執行時,從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(在本教程的JobStore節討論),你應該當心使用JobDataMap,考慮哪些數據要放在JobDataMap中。由於這裏面的對象將會被序列化,所以容易產生類版本問題(原文:they therefore become prone to class-versioning problems.)。顯然標準的Java類型應該是十分安全的, 但除此以外,任什麼時候間一我的改變了你序列化實例的類的定義,你應該當心確保它的兼容性。或者,你能夠把JDBC—JobStore和JobDataMap應用在一個模式中,在這個模式中只有基本類型和String能夠被放在Map中,以此排除任何之後的序列化問題的可能性。

若是你在Job類中添加的Setter方法和JobDataMap中key的名字同樣,那麼Quartz的JobFactory實例將自動獲取這些setter方法,在Job實例化時。

Trigger也能夠有JobDataMap。這在某些場合十分有用。當你有一個Job在Scheduler中用多個觸發器重複執行,根據不一樣的觸發器,你但願給予Job不一樣的數據輸入。

Job執行時經過JobExecutionContext 獲取JobDataMap很方便。在Job中和Trigger中都定義了JobDataMap,那麼這兩個JobDataMap將會合並,對於相同的Key,值取後加載的。

eg1: Job執行時,從JobExecutionContext’s 中獲取合併的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);      
    }      
  }

eg2: 經過JobFactory注入datamap 數據 能夠是這樣的

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實例(Job Instances)

你能夠建立一個Job類,建立多個JobDetail實例存儲多個該類的實例定義,每一個JobDetail有他機子的屬性和JobDataMap,並將它們都添加到Scheduler中。

例如:你能夠建立一個類實現了Job接口,名叫SalesReportJob。這個任務但願經過JobDataMap來傳遞參數來識別銷售的名字,銷售報告須要基於這個名字。它們可能建立多個job的定義(JobDetail),例如SalesReportForJoe,SalesReportForMike,它們有「joe」和"Mike"在JobDataMap中來輸入到各自的Job中。

當一個Trigger觸發時,與他相關的JobDetail(Job的實例定義)將被載入,它相關的Job類將經過JobFactory實例化。默認的JobFactory將調用Job類的newInstance(),而後嘗試調用Job類中setter方法(和JobDataMap中的key值相同)。你能夠建立自定義JobFactory的實現來完成一些功能,例如應用的IoC

每一個存儲了的JobDetail被認爲是Job定義或者JobDetail實例,每一個執行Job認爲是Job實例或者Job定義實例。一般當屬於Job引用時,它一般是指一個Job的定義或者JobDetail。

Job State and Concurrency

有兩個註釋你能夠添加到你的Job類中,這將影響Quartz的行爲。

@DisallowConcurrentExecution - 有這個QUartz將不會同時執行多個給定Job定義的實例。在上一個例子中,若是SalesReportJob 有這個註解,那麼在一個給定的時間,只有一個SalesReportForJoe 實例能夠執行,但能夠同時執行SalesReportForMike實例。約束是基於JobDetail,而不是Job類的實例。但註解是在Job類上的,由於行爲的不一樣是Job類的代碼形成的。

@PersistJobDataAfterExecution - Quartz在成功執行了execute()方法後,JobDetail中JobDataMap將被更新,下一次執行相同job時,使用的是更新後的數據。和@DisallowConcurrentExecution同樣,註解也是定義在Job類上的。

若是你使用了@PersistJobDataAfterExecution註解,你應該考慮同時使用@DisallowConcurrentExecution註解,來避免當同一個job(JobDetail)的兩個實例被併發執行時,因爲競爭,JobDataMap中存儲的數據極可能是不肯定的。

Other Attributes Of Jobs

  • Durability - 若是一個job是非持久的,它將在與它綁定的Scheduler不存在時同時不存在,也就是說他的生命週期是與它綁定的Trigger是相同的。
  • RequestsRecovery - 當一個job在執行時意外關閉,那麼job將再次執行當Scheduler再次啓動時。

JobExecutionException

execute方法中僅容許拋出一種類型的異常(包括RuntimeExceptions),即JobExecutionException。所以,你應該將execute方法中的全部內容都放到一個」try-catch」塊中。你也應該花點時間看看JobExecutionException的文檔,由於你的job可使用該異常告訴scheduler,你但願如何來處理髮生的異常。

另外可參考http://ifeve.com/?s=quartz

相關文章
相關標籤/搜索