您的位置:首页 > 编程语言 > Java开发

[译]Java定时任务调度-Quartz文档(三)进一步讲讲Job和Job Detail

2017-03-21 13:58 549 查看
正如上篇文章所说的,Job很容易实现,只需要接口中唯一的execute方法。除此之外,你还需要稍微了解下Job、execute、Job interface和JobDetail的一些东西。

当你写的Job类执行特定任务时,Quartz需要知道这个类应该具有的各种属性。这就是前面所提到的JobDetail类完成的。

JobDetail实例是由JobBuilder创建的,可以这样引入:

import static org.quartz.JobBuilder.*;


让我们花一点时间来看看Job的原理和Quartz中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 class,它就知道知道所执行的任务类型。每当scheduler执行任务的时候,总是创建一个新的类实例,在执行其execute方法。执行完毕后,会释放对该实例的引用,稍后将会被垃圾回收。这种做法要求Job类必须有一个无参的构造方法,还有就是静态域是无效的,因为无法在多个执行实例中共享。

你可能想问,如何对Job实例配置属性呢?或者换句话说,如何跟踪多个实例的执行呢?答案是使用JobDataMap,它属于JobDetail对象的一部分。

JobDataMap

JobDataMap是用来在执行过程中存储必要的数据对象。JobDataMap实现了Java Map接口,

还定义了一些存取基本数据类型的方法。

下面的例子介绍了在创建JobDetail时向JobDataMap中添加数据:

// 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();


下面的例子介绍在执行过程中如何从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只能存储基本类型和String,这样就排除了序列化带来的潜在问题。

如果你在Job类中定义了与JobDataMap中key对应的setter方法,Quartz的就会在job实例化是调用这些setter方法,所以应避免定义类似的方法,以保证map中数据的准确性。

Triigers也有对应的JobDataMap。当定义一个任务,关联到多分数据、多个触发器,定期、重复执行时,JobDataMap会很有用。

JobDataMap定义在Job执行过程中的JobExecutionContext中,由JobDetail中的JobDataMap和Trigger中的JobDataMap合并而成,如果有重名的变量,前面的将会被后面的所覆盖。

下面是从合并后的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将data 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中的代码却变短了。有人会说虽然总代码量长,但是setter方法都可以用IDE自动生成,所以“手工”的代码实际是更短的,而且不必手动的从JobDataMap中取数据。怎么做,你决定!

Job “Instances”

很多用户花很长时间纠结于是谁创建了job实例。这里我们将就任务的状态和并发等问题,弄个明白。

你可以定义一个job类,然后创建多个JobDetail实例,每个实例都有自己的属性和JobDataMap,然后把它们关联到一个scheduler。

例如,你可以创建一个实现了Job interface的类“SalesReportJob”,这个job需要根据传进来的销售人员的姓名来确定销售报告。接着需要定义多个JobDetail,例如“SalesReportForJoe” and “SalesReportForMike”,并将“joe” and “mike”存入个子的JobDataMap中。

当触发器启动是,与之关联的JobDetail将会被加载,对应的job class会通过scheduler中配置的JobFactory实例化。默认的JobFactory会调用job class的newInstance方法,然后根据JobDataMap中的key值调用对应的setter方法进行变量赋值。你可能想创建自己的JobFactory实现来完成注入自己的IoC(控制反转)或者DI(依赖注入)。

在“Quartz speak”中,我们将存储的JobDetail看做“job definition”或者“JobDetail 实例”。通常来说,提到“job”,我们指的是一个具体的实现,即JobDetail;提到实现job interface的类,通常指的是“job class”。

Job State and Concurrency

接下来再看看任务的状态数据和并发问题。以下是Job类中可以使用的注解,这些注解会影响Quartz的行为。

@DisallowConcurrentExecution 添加到Job class,Quartz就不会并发的执行多个任务实例。

注意这里的用词。在前面的例子中,如果“SalesReportJob”添加了这个注解,那么同时只会有一个“SalesReportForJoe”实例运行,但这并不影响并发的执行“SalesReportForJoe”任务实例。这个约束是在JobDetail维度的,不是在Job class维度。

@PersistJobDataAfterExecution 添加了这个注解的job class,在执行完JobDetail后,会将其JobDataMap备份,以便于下次执行是使用,而不是使用默认的初始化数据。和第一个一样,这个约束也是在JobDetail维度,而不是Job class维度。

如果你使用@PersistJobDataAfterExecution注解,你需要慎重考虑是否要使用@DisallowConcurrentExecution注解,以避免当一个JobDetail并发执行时数据是否会相互影响。

Other Attributes Of Jobs

下面是其他可以通过JobDetail对象定义给job实例的属性归类:

Durability - 如果job是非持久的,与其相关的所有trigger触发执行完成之后将会自动删除,换计划说,所有job都是以与其相关的trigger结束而结束的。

RequestsRecovery - 如果job是“请求恢复”的,如果任务执行过程中schduler强制关闭了,那么当scheduler重新启动时,它将再次执行。在这种情况下JobExecutionContext.isRecovering()将返回TRUE;

JobExecutionException

最后,我们需要讲下Job.execute(..)的细节。在这个方法内部唯一可以排除的异常是JobExecutionException。鉴于此,可能会抛出的异常都应该使用tru/catch捕获处理。你也因该进一步看下JobExecutionException相关文档,以便更好的处理。

原文链接:

http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-03.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java quartz 文档
相关文章推荐