Quartz教程三:Job与JobDetail介绍

原文链接 | 译文链接 | 翻译:nkcoder | 校对:

本系列教程由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 classJobDetail 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,你希望如何来处理发生的异常。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Quartz教程三:Job与JobDetail介绍

转载于:https://www.cnblogs.com/wlsblog/p/8295802.html

Quartz教程三:Job与JobDetail介绍相关推荐

  1. TM4C123GLaunchPad教程三_外部中断

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

  2. 汇川技术小型PLC梯形图编程系列教程(三):PLC系统程序与用户程序介绍

    原文链接:汇川技术小型PLC梯形图编程系列教程(三):PLC系统程序与用户程序介绍 PLC的定义 可编程逻辑控制器是种专门为在工业环境下应用而设计的数字运算操作电子系统.它采用一种可编程的存储器,在其 ...

  3. Java 结构体之 JavaStruct 使用教程三 JavaStruct 数组进阶

    经过前面两篇博客的介绍,相信对于 JavaStruct 的认识以及编程使用,读者已经有一定的基础了.只要理解和实践结合起来,掌握还是很容易的.下面进行一些数组使用方面的实例说明及演示. 在结构体类中使 ...

  4. MongoDB 教程三: 高级查询

    视频:MongoDB 教程三: 高级查询 MongoDB支持的查询语言非常强大,语法规则类似于面向对象的查询语言,可以实现类似关系数据库单表查询的绝大部分功能,并且由于 MongoDB可以支持复杂的数 ...

  5. Hadoop教程(三)HDFS文件系统Shell命令

    Hadoop教程(三)HDFS文件系统Shell命令 本文链接:https://blog.csdn.net/yuan_xw/article/details/50202381 Hadoop教程(三)HD ...

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

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

  7. 区块链教程(二):基础概念介绍

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 本系列重点在于以太坊基础知识.以太坊客户端以及以太坊solidity编程,因此博客重点在于以太坊核心知识点的掌握,区块链部分的基础知识可以作为补充 ...

  8. Fastify 系列教程三 (验证、序列化和生命周期)

    Fastify 系列教程: Fastify 系列教程一 (路由和日志) Fastify 系列教程二 (中间件.钩子函数和装饰器) Fastify 系列教程三 (验证.序列化和生命周期) Fastify ...

  9. x%3e=y%3e=z的c语言表达式,我的C语学习笔记-C语言教程(三).doc

    我的C语学习笔记- C语言教程(三) C语言教程---第一章: C语言概论 C语言教程---第二章: 数据类型.运算符.表达式 C语言教程---第三章: C语言程序设计初步 C语言教程---第四章: ...

最新文章

  1. 开源大数据周刊-2018年08月10日 第96期
  2. Installshield在安装结束时刷新系统
  3. 2021年五月下旬推荐文章(2)
  4. php 声音文件链 处理,PHP 三种方式实现链式操作
  5. postman的参数params如何设置数组和对象参数
  6. 2009年南京辞职当日
  7. 抖音视频评论获取系统,获取抖音评论的系统
  8. 报错command line option ‘-std=c11‘ is valid for C/ObjC but not for C++
  9. eclipse保存文件出现save could not be completed. Try File Save As..
  10. 心情随笔--我与考研战友的故事
  11. 网易企业邮箱注册后,管理员怎么管理邮箱?
  12. 企业技术中心申报条件
  13. 职场PUA:为什么你就不能逼自己一把呢?
  14. Gradual warmup lr schedule--pytorch
  15. 【调剂】北京语言大学 SAIT 智能语音习得技术实验室
  16. 源码学习之Vector
  17. html5+ 原生标题栏添加input 输入框
  18. 流,向量场,和微分方程
  19. 鲁大师5月新机性能榜:红魔6R夺冠,“特供版”新机密集
  20. Matlab中向图中添加文本

热门文章

  1. 游戏中每日刷新实现思路浅析
  2. 四年级下册英语计算机房和教师办公室的图片,PEP英语四年级下册-Unit-1思维导图及知识点梳理.pptx...
  3. python必学的模块_Python常用的模块
  4. hdu5249KPI动态中位数(两个set)
  5. hdu2492 数状数组或者线段树
  6. 【Android 插件化】基于插件化引擎的“恶意应用“与“良性应用“区别 | 恶意插件化应用特征
  7. 【Android 安全】DEX 加密 ( Application 替换 | ActivityThread 中的 mAllApplications 集合添加 Application )
  8. 【Netty】Netty 核心组件 ( Pipeline | ChannelPipeline )
  9. Touch Bar 废物利用系列 | 在触控栏上显示 Dock 应用图标
  10. 记一次Oracle数据故障排除过程