本系列教程由quartz-2.2.x官方文档翻译、整理而来,希望给同样对quartz感兴趣的朋友一些参考和帮助,有任何不当或错误之处,欢迎指正;有兴趣研究源码的同学,可以参考我对quartz-core源码的注释(进行中)。

正如在教程二中讲到的,Job实现起来很容易,该接口只有一个“execute”方法。本节主要关注:Job的特点、Job接口的execute方法以及JobDetail。

你定义了一个实现Job接口的类,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成。

JobDetail实例是通过JobBuilder类创建的,导入该类下的所有静态方法,会让你编码时有DSL的感觉:

import static org.quartz.JobBuilder.*;

让我们先看看Job的特征(nature)以及Job实例的生命期。不妨先回头看看教程一中的代码片段:

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

}

}

可以看到,我们传给scheduler一个JobDetail实例,因为我们在创建JobDetail时,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行何种类型的job;每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另一个后果是,在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。

那么如何给job实例增加属性或配置呢?如何在job的多次执行中,跟踪job的状态呢?答案就是:JobDataMap,JobDetail对象的一部分。

JobDataMap

JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

将job加入到scheduler之前,在构建JobDetail时,可以将数据放入JobDataMap,如下示例:

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部分会讲到),在决定JobDataMap中存放什么数据的时候需要小心,因为JobDataMap中存储的对象都会被序列化,因此很可能会导致类的版本不一致的问题;Java的标准类型都很安全,如果你已经有了一个类的序列化后的实例,某个时候,别人修改了该类的定义,此时你需要确保对类的修改没有破坏兼容性;更多细节,参考现实中的序列化问题。另外,你也可以配置JDBC-JobStore和JobDataMap,使得map中仅允许存储基本类型和String类型的数据,这样可以避免后续的序列化问题。

如果你在job类中,为JobDataMap中存储的数据的key增加set方法(如在上面示例中,增加setJobSays(String val)方法),那么Quartz的默认JobFactory实现在job被实例化的时候会自动调用这些set方法,这样你就不需要在execute()方法中显式地从map中取数据了。

在Job执行时,JobExecutionContext中的JobDataMap为我们提供了很多的便利。它是JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值。

下面的示例,在job执行时,从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()方法中的代码更简洁了。而且,虽然代码更多了,但如果你的IDE可以自动生成setter方法,你就不需要写代码调用相应的方法从JobDataMap中获取数据了,所以你实际需要编写的代码更少了。当前,如何选择,由你决定。

Job实例

很多用户对于Job实例到底由什么构成感到很迷惑。我们在这里解释一下,并在接下来的小节介绍job状态和并发。

你可以只创建一个job类,然后创建多个与该job关联的JobDetail实例,每一个实例都有自己的属性集和JobDataMap,最后,将所有的实例都加到scheduler中。

比如,你创建了一个实现Job接口的类“SalesReportJob”。该job需要一个参数(通过JobdataMap传入),表示负责该销售报告的销售员的名字。因此,你可以创建该job的多个实例(JobDetail),比如“SalesReportForJoe”、“SalesReportForMike”,将“joe”和“mike”作为JobDataMap的数据传给对应的job实例。

当一个trigger被触发时,与之关联的JobDetail实例会被加载,JobDetail引用的job类通过配置在Scheduler上的JobFactory进行初始化。默认的JobFactory实现,仅仅是调用job类的newInstance()方法,然后尝试调用JobDataMap中的key的setter方法。你也可以创建自己的JobFactory实现,比如让你的IOC或DI容器可以创建/初始化job实例。

在Quartz的描述语言中,我们将保存后的JobDetail称为“job定义”或者“JobDetail实例”,将一个正在执行的job称为“job实例”或者“job定义的实例”。当我们使用“job”时,一般指代的是job定义,或者JobDetail;当我们提到实现Job接口的类时,通常使用“job类”。

Job状态与并发

关于job的状态数据(即JobDataMap)和并发性,还有一些地方需要注意。在job类上可以加入一些注解,这些注解会影响job的状态和并发性。

@DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。请注意这里的用词。拿前一小节的例子来说,如果“SalesReportJob”类上有该注解,则同一时刻仅允许执行一个“SalesReportForJoe”实例,但可以并发地执行“SalesReportForMike”类的一个实例。所以该限制是针对JobDetail的,而不是job类的。但是我们认为(在设计Quartz的时候)应该将该注解放在job类上,因为job类的改变经常会导致其行为发生变化。

@PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。和 @DisallowConcurrentExecution注解一样,尽管注解是加在job类上的,但其限制作用是针对job实例的,而不是job类的。由job类来承载注解,是因为job类的内容经常会影响其行为状态(比如,job类的execute方法需要显式地“理解”其”状态“)。

如果你使用了@PersistJobDataAfterExecution注解,我们强烈建议你同时使用@DisallowConcurrentExecution注解,因为当同一个job(JobDetail)的两个实例被并发执行时,由于竞争,JobDataMap中存储的数据很可能是不确定的。

Job的其它特性

通过JobDetail对象,可以给job实例配置的其它属性有:

Durability:如果一个job是非持久的,当没有活跃的trigger与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;

RequestsRecovery:如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true。

JobExecutionException

最后,是关于Job.execute(..)方法的一些额外细节。execute方法中仅允许抛出一种类型的异常(包括RuntimeExceptions),即JobExecutionException。因此,你应该将execute方法中的所有内容都放到一个”try-catch”块中。你也应该花点时间看看JobExecutionException的文档,因为你的job可以使用该异常告诉scheduler,你希望如何来处理发生的异常。

quartzjob传递传输_Quartz教程三:Job与JobDetail介绍相关推荐

  1. TM4C123GLaunchPad教程三_外部中断

    TM4C123GLaunchPad教程三_外部中断 介绍 ​ TM4C的每个IO口都可以分别配置外部中断,每组IO口对应一个中断服务函数,例如PA0-PA7对应同一个中断服务函数,每个IO口可以配置为 ...

  2. Docker教程(三) Docker网络访问和数据管理

    Docker教程(三) Docker网络访问和数据管理 本文链接:https://blog.csdn.net/yuan_xw/article/details/77504077 Docker教程(三) ...

  3. 区块链教程(三):Solidity编程基础

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 区块连教程(一):前置知识-linux补充 区块链教程(二):基础概念介绍 区块链教程(三):Solidity编程基础 区块链教程(四):搭建私链 ...

  4. python爬虫入门教程(三):淘女郎爬虫 ( 接口解析 | 图片下载 )

    2019/10/28更新 网站已改版,代码已失效(其实早就失效了,但我懒得改...)此博文仅供做思路上的参考 代码使用python2编写,因已失效,就未改写成python3 爬虫入门系列教程: pyt ...

  5. Spring认证中国教育管理中心-Spring Data Redis框架教程三

    原标题:Spring认证中国教育管理中心-Spring Data Redis框架教程三 10.15.支持类 Packageorg.springframework.data.redis.support提 ...

  6. Quartz教程三:Job与JobDetail介绍

    Quartz教程三:Job与JobDetail介绍 原文链接 | 译文链接 | 翻译:nkcoder | 校对: 本系列教程由quartz-2.2.x官方文档翻译.整理而来,希望给同样对quartz感 ...

  7. jQuery 3教程(三):jQuery集合

    原文地址:jQuery 3教程(三):jQuery集合 Introduction jQuery选择器选择出来的结果很多时候是一个DOM元素集而非单个元素,jQuery可以灵活的访问和修改DOM元素集, ...

  8. Swift中文教程(三)--流程控制

    Swift中文教程(三)--流程控制 原文:Swift中文教程(三)--流程控制 Swift用if和switch编写条件控制语句,用for-in,for,while和do-while编写循环.条件控制 ...

  9. qpython3可视图形界面_PySide——Python图形化界面入门教程(三)

    PySide--Python图形化界面入门教程(三) --使用内建新号和槽 --Using Built-In Signals and Slots 上一个教程中,我们学习了如何创建和建立交互widget ...

最新文章

  1. VS2017 Pro未能找到路径“……\bin\roslyn\csc.exe”的解决方案
  2. unity怪物攻击玩家减血_热血传奇:游戏里要千万小心的小怪,玩家稍有不慎就要吃大亏...
  3. 文献记录(part81)--Clustering-based k -nearest neighbor classification for large-scale data with ...
  4. PowerTip of the Day-Removing Empty Things
  5. mysql数据库原理及应用实训报告_《数据库原理及应用》实验报告.doc
  6. OneNote for windows10 鼠标点击没反应
  7. 微信小程序-传统开发模式实现授权注册登录流程【超详细,附源码】
  8. iOS学习重要知识点整理02-进程和线程的一个简单解释
  9. html点击出现表单弹窗
  10. 2022年全球市场次氯酸钠总体规模、主要生产商、主要地区、产品和应用细分研究报告
  11. 拼多多崛起背后的电商新版图
  12. 003基于神经网络的癫痫脑电信号检测与分类
  13. 刚子扯谈:要明白网站建设的真正意义
  14. 【LabVIEW小技巧】LabVIEW文本框显示VI名称
  15. PHP 大型网站高并发大流量解决方案
  16. 这篇文章教你把word文件转为图片
  17. vue移动端点击复制文本内容
  18. 微信小程序入门到实战(三)
  19. 皮皮安学Java第二十六天
  20. 软考高项 : 信息系统项目管理师模拟试题

热门文章

  1. mysql数据存储7天_MySQL开启binlog并且保存7天有效数据
  2. Python如何在循环语句中加入两个变量_python学习笔记(第一章)
  3. 滴滴宋世君:DS(数据分析师),究竟是做什么的?
  4. IPython的一些使用技巧
  5. SAP Spartacus 服务器端渲染的三种可能情形
  6. TypeScript 里的 module 解析过程 - Module Resolution
  7. Angular 项目 tsconfig.json 里定义的 out-tsc 还有作用吗?
  8. SAP Spartacus cxFocus增添了refresh Focus功能后的一些考虑
  9. ExpressionChangedAfterItHasBeenCheckedError - Expression has changed after it was checked
  10. SAP CDS view里将Date和time连接成timestamp的函数