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

相关文章
相关标签/搜索