从上面两篇文章看出,Job 的实现很简单,该接口只有一个 execute 方法。这一节我们主要关注以下三点:
首先我们回过头看看 教程一 中的代码片段
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("dummyJobName", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("dummyTriggerName", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5).repeatForever()).build();
sched.scheduleJob(job, trigger);
HelloJob类定义如下
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date())+"Hello Quartz!");
}
}
可以看到,我们传给 scheduler 一个 JobDetail 实例,因为我们在创建 JobDetail 时,将要执行的 job 的类名传给了 JobDetail,所以 scheduler 就知道了要执行何种类型的 job;
当 scheduler 执行 job 时,在调用其 execute() 方法之前会创建该类的一个新的实例,执行完毕,该实例的饮用就会被丢弃,实例会被垃圾回收。这种执行策略带来的后果就是,job 必须有一个无参的构造函数;另一个是,在 job 类中,不应该定义有状态的数据属性,因为 job 多次执行中,这些属性的值不会保留。
那么如何给 job 实例增加属性或配置呢?如何在 job 多次执行中,跟踪 job 的状态呢?答案就是 JobDataMap
JobDataMapMap 中可以包含不限量的(序列化)数据对象,在 job 实例执行的时候,可以使用其中的数据;JobDataMap 是 Java Map 接口的一个实现,额外增加了一些便于存取的基本数据类型的方法。
将 job 加入到 scheduler 之前,在构建 JobData 时,可以将数据放入 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);
}
}
如果你使用的是持久化存储机制,再决定 JobDataMap 中存放什么数据的时候需要小心,因为 JobDataMap 中存储对象都会被序列化,因此很可能会导致类的版本不一致问题;Java 的标准类型都很安全,如果一斤有一个类的序列化后的实例,某个时候,鄙人修改了该类的定义,此时你需要确保对类的修改没有破坏兼容性;
你可以只创建一个 job 类,然后创建多个与该 job 类关联的 JobDetail 实例,每一个实例都有自己的属性集和 JobDataMap,最后,将所有的实例都加到 scheduler中。
比如,你创建了一个实现 Job 接口的类 “SaleReportJob” 。该 job 需要一个参数(通过 JobDataMap 传入),表示负责该销售报告的销售员名字。因此,你可以创建该 job 的多个实例(JobDetail),比如“SalesReportForJoe”、“SalesReportForMike”。将 “joe”、“mike” 作为 JobDataMap 的数据传给对应的 job 实例。
当一个 tigger 被触发时候,与之关联的 JobDetail 实例会被加载,JobDetail 引用的 job 类通过配置在 Scheduler 上的 JobFactory 进行初始化。默认的 JobFactory 实现,仅仅是调用 job 的 newInstance() 方法,然后尝试调用 JobDataMap 中的 key 的 setter 方法。