由上章节可知,Job接口是具体的任务逻辑类必须要实现的接口
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
StringJoiner joiner=new StringJoiner(" ")
.add("hello")
.add(DateUtil.getDateTime(new Date()))
.add(Thread.currentThread().getName())
.add(jobExecutionContext.getTrigger().getKey().getName());
System.out.println(joiner.toString());
}
}
在JobBuilder创建JobDetail实例时,我们通过传递任务类型的方式告知触发器将要触发的任务。
// newJob方法是JobBuilder类提供的静态方法
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
每次触发时,通过任务类型的无参数构造函数实例化HelloJob 对象,然后再执行实例对象中的execute方法。任务结束后,这个任务实例对象将被垃圾回收器回收。
为了演示每次触发时,quartz框架都会重新创建任务实例,HelloJob中增加无参数构造方法
public HelloJob(){
System.out.println("我被构造了"+this.hashCode());
}
同样使用上篇文章中的代码样例 quartz(1),此时控制台输出
2022-09-26 19:36:24.546 INFO 2212 ---
我被构造了10703978
hello 2022-09-26 19:36:24
我被构造了10692626
hello 2022-09-26 19:36:27
我被构造了22832069
hello 2022-09-26 19:36:30
我被构造了9617674
hello 2022-09-26 19:36:33
我被构造了14815745
hello 2022-09-26 19:36:36
.......
通过输出可知每次调用execute前,均先实例化HelloJob对象【不是同一个对象,hashcode码不一样】。无参数构造方法意味着HelloJob任务的执行中不包含任务的信息,意味着这是一个无状态的任务
与任务的执行过程中,不包含任何外来的参数信息相反,有状态任务意味着任务的执行需要外部的条件,比如有一个高温预警任务,需要判定当前温度是否为高温(大于39摄氏度),并决定是否发送短信通知。这里就有一个参数决定任务的执行逻辑,当前温度。显然我们只能通过无参数构造的方法实例化任务逻辑对象,但是我们可以用JobDataMap存放Job执行的外部信息。
JobDataMap 可用于保存您希望在作业实例执行时可供其使用的任意数量的(可序列化)数据对象。JobDataMap 是 Java 映射接口的实现,并且增加了一些用于存储和检索基元类型数据的便捷方法。
JobDetail和Trigger中都可以包含自己的JobDataMap
实例代码如下
public class HelloDataJobimplements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
StringJoiner joiner=new StringJoiner(" ")
.add("hello every one")
.add("I am "+jobExecutionContext.getJobDetail().getJobDataMap().get("name"))
.add("I am "+jobExecutionContext.getJobDetail().getJobDataMap().get("age"))
.add(DateUtil.getDateTime(new Date()))
.add("triggered by "+jobExecutionContext.getTrigger().getJobDataMap().get("trigger_name"));
System.out.println(joiner.toString());
}
}
@Test
void moreAboutJob(){
try {
// Grab the Scheduler instance from the Factory
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// and start it off
scheduler.start();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloDataJob.class)
.withIdentity("job1", "group1")
.usingJobData("name","lyf")
.usingJobData("age",19)
.build();
// Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("trigger_name","trigger1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
Thread.sleep(10000);
scheduler.shutdown();
} catch (SchedulerException | InterruptedException se) {
se.printStackTrace();
}
}
hello every one I am lyf I am 19 2022-09-26 20:29:42 triggered by trigger1
hello every one I am lyf I am 19 2022-09-26 20:29:45 triggered by trigger1
hello every one I am lyf I am 19 2022-09-26 20:29:48 triggered by trigger1
hello every one I am lyf I am 19 2022-09-26 20:29:51 triggered by trigger1
首先得出结论,一个Job可被多个JobDetail同时引用,每个JobDetail中可存放具体实例定义的信息。这样设计的原因在于,每个任务逻辑执行的条件可能不同,那么就必须根据实际情形描述每个任务执行的其他信息。
比如,你可以创建一个类来实现名为“销售报告作业”的作业。可以对作业进行编码,以期望发送给它的参数(通过 JobDataMap)指定销售报表应基于的销售人员的姓名。然后,他们可以创建作业的多个定义(工作详细信息),例如“Joe”和“mike”
当触发器触发时,将加载它所关联的 JobDetail(实例定义),并通过计划程序上配置的 JobFactory 实例化它所引用的作业类。默认的作业工厂只是在作业类上调用 newInstance(),然后尝试调用与 JobDataMap 中的键名称匹配的类上的 setter 方法。也可能希望创建自己的 JobFactory 实现来完成一些事情,例如让应用程序的 IoC 或 DI 容器生成/初始化作业实例。