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

原文地址: http://www.quartz-scheduler.o...

第三课: Jobs 与 Job Details 更多相关html

正如你在第二课所见,任务实现起来至关简单,仅仅有一个 'execute' 方法在接口。这里仅有少数东西你须要明白关于jobs的本质,关于Job 接口 execute(...) 方法,还有 JobDetails。java

当你实现一个任务类,有知道如何作特定类型工做的代码,Quartz 须要了解关于各类你但愿任务实例拥有的属性。这个就经过JobDetail完成,在上一节已经简单提过。编程

JobDetail 实例是经过 JobBuilder 类构建的。你一般能够用一个静态导入所有它的方法,为了让你的代码看起来有领域规范语言的感受。安全

import static org.quartz.JobBuilder.*;

让咱们花一点时间讨论一点 Jobs的本性和 job实例的在Quartz的生命周期。首先让咱们回头看一眼咱们在第一课的代码片断:并发

// 定义任务并绑定咱们的 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" 定义以下:less

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 实现时)。另外一个结果就是在做业类上定义状态数据字段是没有意义的-他们的值不会在任务执行期间被保存。ui

你可能会想问“我怎样才能为一个任务实例保存属性/配置?” 还有 “我怎样才能跟踪一个任务的状态在执行之间?”他们的答案都是同样的,关键是JobDataMap, JobDetail对象的一部分。this

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 发布!
相关文章
相关标签/搜索