[译]Java定时任务调度-Quartz文档(三)进一步讲讲Job和Job Detail
2017-03-21 13:58
549 查看
正如上篇文章所说的,Job很容易实现,只需要接口中唯一的execute方法。除此之外,你还需要稍微了解下Job、execute、Job interface和JobDetail的一些东西。
当你写的Job类执行特定任务时,Quartz需要知道这个类应该具有的各种属性。这就是前面所提到的JobDetail类完成的。
JobDetail实例是由JobBuilder创建的,可以这样引入:
让我们花一点时间来看看Job的原理和Quartz中Job的生命周期。首先看一下在第一篇文章中提及的一块代码片段:
HelloJob的定义如下:
注意传给scheduler的是一个JobDetail的实例,仅仅通过在创建JobDetail是指定了Job class,它就知道知道所执行的任务类型。每当scheduler执行任务的时候,总是创建一个新的类实例,在执行其execute方法。执行完毕后,会释放对该实例的引用,稍后将会被垃圾回收。这种做法要求Job类必须有一个无参的构造方法,还有就是静态域是无效的,因为无法在多个执行实例中共享。
你可能想问,如何对Job实例配置属性呢?或者换句话说,如何跟踪多个实例的执行呢?答案是使用JobDataMap,它属于JobDetail对象的一部分。
还定义了一些存取基本数据类型的方法。
下面的例子介绍了在创建JobDetail时向JobDataMap中添加数据:
下面的例子介绍在执行过程中如何从JobDataMap中取数据:
如果你使用持久化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中取值的一个例子:
如果你想使用JobFactory将data map中的变量“注入”到你的类中,可以这样做:
你应该能注意到,下面的代码总的来说更长,但是execute中的代码却变短了。有人会说虽然总代码量长,但是setter方法都可以用IDE自动生成,所以“手工”的代码实际是更短的,而且不必手动的从JobDataMap中取数据。怎么做,你决定!
你可以定义一个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”。
@DisallowConcurrentExecution 添加到Job class,Quartz就不会并发的执行多个任务实例。
注意这里的用词。在前面的例子中,如果“SalesReportJob”添加了这个注解,那么同时只会有一个“SalesReportForJoe”实例运行,但这并不影响并发的执行“SalesReportForJoe”任务实例。这个约束是在JobDetail维度的,不是在Job class维度。
@PersistJobDataAfterExecution 添加了这个注解的job class,在执行完JobDetail后,会将其JobDataMap备份,以便于下次执行是使用,而不是使用默认的初始化数据。和第一个一样,这个约束也是在JobDetail维度,而不是Job class维度。
如果你使用@PersistJobDataAfterExecution注解,你需要慎重考虑是否要使用@DisallowConcurrentExecution注解,以避免当一个JobDetail并发执行时数据是否会相互影响。
Durability - 如果job是非持久的,与其相关的所有trigger触发执行完成之后将会自动删除,换计划说,所有job都是以与其相关的trigger结束而结束的。
RequestsRecovery - 如果job是“请求恢复”的,如果任务执行过程中schduler强制关闭了,那么当scheduler重新启动时,它将再次执行。在这种情况下JobExecutionContext.isRecovering()将返回TRUE;
原文链接:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-03.html
当你写的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文档(七)TriggerListeners and JobListeners
- [译]Java定时任务调度-Quartz文档(二)Quartz API、Job & Scheduler
- Java定时任务调度工具详解(4)— Quartz 之 Job/JobDetail/JobExecutionContext/JobDataMap
- java中quartz 调度在一些定时任务(job)的入门级应用
- [译]Java定时任务调度-Quartz文档(八)SchedulerListeners
- [译]Java定时任务调度-Quartz文档(九)Job Stores
- [译]Java定时任务调度-Quartz文档(一)初步使用
- [译]Java定时任务调度-Quartz文档(十)Configuration, Resource Usage and SchedulerFactory
- (转)Java任务调度框架Quartz入门教程指南(二) 使用job、trigger、schedule调用定时任务
- [译]Java定时任务调度-Quartz文档(十二)Miscellaneous Features of Quartz
- [译]Java定时任务调度-Quartz文档(五)SimpleTrigger
- Java任务调度框架Quartz入门教程指南(二) 使用job、trigger、schedule调用定时任务
- [译]Java定时任务调度-Quartz文档(六)CronTrigger
- [译]Java定时任务调度-Quartz文档(四)More About Triggers
- Java定时任务调度工具详解之Quartz篇(中级)一:浅谈JobExecutionContext&JobDatai&浅谈Trigger
- [译]Java定时任务调度-Quartz文档(十一)Advanced (Enterprise) Features
- Java定时任务调度之Quartz
- Java定时任务调度工具详解(7)— Quartz 之 Scheduler
- SpringBoot整合Quartz定时任务 系统job Spring Boot教程 调度任务
- Java定时任务调度工具详解之Quartz篇(中级)二:SimpleTrigger& CronTrigger&浅谈Scheduler&QuartzProperties文件