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

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

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

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

当你实现一个任务类,有知道如何做特定类型工作的代码,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" 定义如下:


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对象的一部分。

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 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容