原文地址: 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
是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 对象的其余属性:
最后,咱们须要告知你 Job.execute(...)
的一些细节。惟一的一种异常类型(包含RuntimeEcxeption) 你能够从执行方法中抛出的异常是 JobExecutionException。 正是由于这样,你应该一般包装所有内容在“try-catch”块中。你还应该花点时间阅读文档中,关于JobExecutionExcetion的部分,为调度程序提供各类指令,以肯定您但愿如何处理异常。
本文由博客一文多发平台 OpenWrite 发布!