- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
虽然您实现的作业类具有知道如何完成特定类型作业的实际工作的代码,但 Quartz 需要了解您可能希望该作业的实例拥有的各种属性。这是通过JobDetail 类完成的,该类在前一节中简要介绍过。
JobDetail 实例是使用 JobBuilder 类构建的。您通常希望使用所有方法的静态导入,以便在代码中有 dsl-feel。
import static org.quartz.JobBuilder.*;
开始之前让我们回顾下原来的代码:
// define the job and tie it to our MyJob class
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("job1", "group1")
.build();
// Trigger the job to run now, and then repeat every 5 seconds
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5)
.repeatForever())
.build();
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// and start it off
scheduler.start();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
请注意,我们给调度程序提供了一个 JobDetail 实例,并且它知道在构建 JobDetail 时只需提供作业的类就可以执行的工作类型。
调度程序执行任务时,每个(以及每个)时间都在调用它的 execute(..)
方法之前创建一个类的新实例。
当执行完成时,对作业类实例的引用被删除,然后实例被垃圾收集。
这种行为的一个分支是,作业必须有一个无参数的构造函数(当使用默认的JobFactory实现时)。
另一个分支是,在作业类上定义状态数据字段是没有意义的,因为它们的值不会在作业执行之间保留。
这些问题的答案是一样的:关键是 JobDataMap,它是 JobDetail 对象的一部分。
JobDataMap 可以用来保存任何数量(可序列化)的数据对象,您希望在它执行时将它们提供给作业实例。
JobDataMap 是 Java Map 接口的一个实现,它提供了一些用于存储和检索原始类型数据的便利方法。
这里有一些快速的片段,可以将数据输入 JobDataMap,同时定义/构建 JobDetail,然后将作业添加到调度器:
// define the job and tie it to our DumbJob class
JobDetail job = newJob(DumbJob.class)
.withIdentity("myJob", "group1") // name "myJob", group "group1"
.usingJobData("jobSays", "Hello World!")
.usingJobData("myFloatValue", 3.141f)
.build();
执行时我们可以将这些数据拿到,方式如下:
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.out.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
如果您使用持久的 JobStore(在本教程的JobStore部分中讨论),您应该在确定JobDataMap中的位置时使用一些谨慎,因为它中的对象将被序列化,因此它们容易出现类版本问题。
显然,标准的Java类型应该是非常安全的,但是除此之外,任何时候如果有人更改了您已经序列化实例的类的定义,就必须注意不要破坏兼容性。您可以选择将 JDBC-JobStore 和 JobDataMap 放到一个模式中,其中只有原语和字符串被允许存储在映射中,从而消除了以后序列化问题的可能。
如果你添加 setter 方法工作类,对应键的名字 JobDataMap(如setJobSays数据(字符串val)方法在上面的示例中),然后 Quartz 默认 JobFactory 实现将自动调用这些 setter 当工作被实例化,从而防止需要显式地获得值的 map 在你的执行方法。
触发器还可以有与之关联的 JobDataMaps。如果您有一个在调度器中存储的作业,它可以通过多个触发器定期/重复地使用,但是每个独立触发,您想要提供不同数据输入的作业,这一点很有用。
在作业执行期间在 JobExecutionContext 中找到的 JobDataMap 是很方便。它是 JobDataMap 中找到的 JobDataMap 的合并,以及在触发器中找到的,后者的值在前者中重写任何相同的值。
下面是在作业执行过程中获取 JobExecutionContext 的合并 JobDataMap 数据的一个简单示例:
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException
{
JobKey key = context.getJobDetail().getKey();
// Note the difference from the previous example
JobDataMap dataMap = context.getMergedJobDataMap();
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;
}
}
这样代码看起来变多了(实际上 setter 方法都是 IDE自动生成的),但是 execute()
可以变得非常简洁。
许多用户花时间搞清楚到底是什么构成了“工作实例”(Job Instances)。我们将在这里和下面的章节中明确说明工作状态和并发性。
您可以创建一个作业类,并通过创建多个 JobDetails 实例(每个实例都有自己的属性和 JobDataMap )并将它们全部添加到调度器中,从而在调度器中存储许多“实例定义”。
例如,您可以创建一个实现名为 “SalesReportJob” 的作业接口的类。该作业可能被编码为期望发送给它的参数(通过 JobDataMap )来指定销售报告应该基于的销售人员的名称。然后,他们可能会创建工作的多个定义( JobDetails ),
例如 “SalesReportForJoe” 和 “SalesReportForMike”,它们在相应的 JobDataMaps 中指定了 “joe” 和 “mike”,作为各自工作的输入。
当触发器触发时,JobDetail(实例定义)将被加载,并且它引用的作业类通过调度程序中配置的JobFactory实例化。默认的JobFactory只调用作业类上的 newInstance()
,然后尝试在类上调用 setter
方法来匹配JobDataMap中的密钥名。您可能希望创建自己的JobFactory实现来完成一些事情,比如让应用程序的IoC或DI容器生成/初始化作业实例。
在“Quartz speak”中,我们将每个存储的JobDetail引用为“作业定义”或“JobDetail实例”,我们将每个执行任务称为“作业实例”或“作业定义的实例”。通常,如果我们只使用“job”这个词,我们指的是一个命名的定义,或者JobDetail。当我们引用实现作业接口的类时,通常使用“作业类”这个术语。
现在,关于作业的状态数据(即JobDataMap)和并发性的一些附加说明。有一些注释可以添加到您的作业类中,这些注释会影响Quartz在这些方面的行为。
@DisallowConcurrentExecution 是一个注释,可以添加到工作类,告诉 Quartz 不要执行一个给定的工作定义的多个实例(指的是给定的工作类)。
注意那里的措辞,因为它是精心挑选的。在前一节的示例中,如果“SalesReportJob”有这个注释,那么“SalesReportForJoe”的一个实例可以在给定的时间执行,但是它可以同时执行“SalesReportForMike”的实例。约束基于实例定义(JobDetail),而不是作业类的实例。然而,决定(在Quartz的设计过程中)将注释进行到类本身,因为它常常对类的编码方式产生影响。
@PersistJobDataAfterExecution 是一个注释,可以添加到工作类告诉 Quartz 更新存储复制JobDetail JobDataMap的 execute()
方法成功完成后(没有抛出异常),这样下一个执行同样的工作(JobDetail)收到更新后的值而不是原先存储的值。像@DisallowConcurrentExecution注释,这适用于工作定义实例,类实例不是一份工作,虽然这是决定工作类的属性,因为它经常改变类是如何编码的(例如,“有状态性”需要显式地“理解”的代码在执行方法)。
如果你使用 @PersistJobDataAfterExecution
注释,您还应该考虑使用 @DisallowConcurrentExecution
注释,以避免可能的混乱(竞争条件)的数据被存储在相同的工作的两个实例(JobDetail)并发执行。
下面是通过JobDetail对象为作业实例定义的其他属性的快速摘要:
如果一个工作是非持久的,它会自动从调度器中删除,一旦不再有任何与它相关联的活动触发器。换句话说,非耐久的工作的生命周期是由其触发器的存在所限制的。
如果一个作业“请求恢复”,并且它在调度程序的“硬关闭”(hard shutdown)期间执行(即它在崩溃中运行的进程,或者机器被关闭),那么当调度程序再次启动时,它将被重新执行。在这种情况下, JobExecutionContext.isRecovering()
方法将返回true。
最后,我们需要通知您一些关于 Job.execute(..)
方法的细节。允许从execute方法抛出的惟一异常(包括runtimeexception)是JobExecutionException。
因此,通常应该用“try-catch”块将execute方法的整个内容包装起来。
您还应该花些时间查看JobExecutionException的文档,因为您的作业可以使用它来为调度程序提供各种指示,以了解如何处理异常。
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improve
介绍篇 什么是MiniApis? MiniApis的特点和优势 MiniApis的应用场景 环境搭建 系统要求 安装MiniApis 配置开发环境 基础概念 MiniApis架构概述
我正在从“JavaScript 圣经”一书中学习 javascript,但我遇到了一些困难。我试图理解这段代码: function checkIt(evt) { evt = (evt) ? e
package com.fastone.www.javademo.stringintern; /** * * String.intern()是一个Native方法, * 它的作用是:如果字
您会推荐哪些资源来学习 AppleScript。我使用具有 Objective-C 背景的传统 C/C++。 我也在寻找有关如何更好地开发和从脚本编辑器获取更快文档的技巧。示例提示是“查找要编写脚本的
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
关闭。这个问题不符合 Stack Overflow guidelines 。它目前不接受答案。 想改善这个问题吗?更新问题,以便堆栈溢出为 on-topic。 6年前关闭。 Improve this
我是塞内加尔的阿里。我今年60岁(也许这是我真正的问题-笑脸!!!)。 我正在学习Flutter和Dart。今天,我想使用给定数据模型的列表(它的名称是Mortalite,请参见下面的代码)。 我尝试
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 9年前关闭。 Improve this que
学习 Cappuccino 的最佳来源是什么?我从事“传统”网络开发,但我对这个新框架非常感兴趣。请注意,我对 Objective-C 毫无了解。 最佳答案 如上所述,该网站是一个好地方,但还有一些其
我正在学习如何使用 hashMap,有人可以检查我编写的这段代码并告诉我它是否正确吗?这个想法是有一个在公司工作的员工列表,我想从 hashMap 添加和删除员工。 public class Staf
我正在尝试将 jQuery 与 CoffeScript 一起使用。我按照博客中的说明操作,指示使用 $ -> 或 jQuery -> 而不是 .ready() 。我玩了一下代码,但我似乎无法理解我出错
还在学习,还有很多问题,所以这里有一些。我正在进行 javascript -> PHP 转换,并希望确保这些做法是正确的。是$dailyparams->$calories = $calories;一条
我目前正在学习 SQL,以便从我们的 Magento 数据库制作一个简单的 RFM 报告,我目前可以通过导出两个查询并将它们粘贴到 Excel 模板中来完成此操作,我想摆脱 Excel 模板。 我认为
我知道我很可能会因为这个问题而受到抨击,但没有人问,我求助于你。这是否是一个正确的 javascript > php 转换 - 在我开始不良做法之前,我想知道这是否是解决此问题的正确方法。 JavaS
除了 Ruby-Doc 之外,哪些来源最适合获取一些示例和教程,尤其是关于 Ruby 中的 Tk/Tile?我发现自己更正常了 http://www.tutorialspoint.com/ruby/r
我只在第一次收到警告。这正常吗? >>> cv=LassoCV(cv=10).fit(x,y) C:\Python27\lib\site-packages\scikit_learn-0.14.1-py
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
我是一名优秀的程序员,十分优秀!