1.关键接口
- Scheduler,任务调度的API;它可以用来启动或者终止任务等。
- Job,具体的任务接口;通过实现它,来让任务调度执行具体的任务。
- JobDetail ,用来定义Job的实例。
- Trigger ,触发器用来定义给定的Job应该如何执行。
- JobBuilder ,用来定义/构建Jobdetail实例。
- TriggerBuilder ,用来定义/构建Trigger实例。
2.简单例子
下面是一个简单的例子,创建一个简单的任务调度。
创建一个Job,名为HelloQuartzJob:
public class HelloQuartzJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("Hello, Quartz! "+ formatter.format(new Date()) + " by " + context.getTrigger()); } }
实现Job接口中的execute方法,这个方法中是我们需要任务调度执行的具体内容。
然后我们就可以编写一个测试类,来执行Job。
public class HelloQuartzScheduling { public static void main(String[] args) throws SchedulerException { //创建JobDetail JobDetail jobDetail = JobBuilder.newJob(HelloQuartzJob.class) .withIdentity("hello","group1") .build(); //创建Trigger Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger","group1") .startNow() .withSchedule( SimpleScheduleBuilder.simpleSchedule() //每5s运行一次 .withIntervalInSeconds(5) //重复运行3次 .withRepeatCount(3) ).build(); //获取Scheduler,并启动任务 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); //添加job,以及其关联的trigger scheduler.scheduleJob(jobDetail, trigger); //启动job scheduler.start(); } }
运行结果,如下:
Jobs与JobDetails
正如第一篇文章看到的那样,实现一个Job是非常简单的,只需要完成execute()方法就行了。
那么Jobs与JobDetails有什么关系呢?简而言之,Job是对任务的一个具体实现;谁执行了这个任务呢?这就需要JobDetail来实现,所以JobDetail就是Job的一个具体实例;如9点上语文课是一个具体任务,而刘老师在9点上语文课就是这个任务的一个具体实例。
Scheduler执行Job时,在调用execute()方法前会先实例化Job;一旦执行结束,该实例就会被丢弃,然后被垃圾回收。
需要注意的是Job必须有一个无参的构造器;另外在Job类中定义数据属性是没有意义的,因为这些属性值并不会在执行期间保存。
JobDataMap
上面说到Job中无法定义属性来传递数据,那么如果我们需要传递数据该怎么做?就是使用JobDataMap,它是JobDetail的一个属性。JobDataMap是Map接口的一个实现,并且它有一些便利的方法来储存和检索基本类型数据。
修改之前的测试类如下:
//创建JobDetailJobDetail jobDetail = JobBuilder.newJob(HelloQuartzJob.class) .withIdentity("hello","group1") .usingJobData("name", "sky") .build();
添加一个值为sky的name属性。然后在HelloQuartzJob中,我们就可以获取到该属性:
public class HelloQuartzJob implements Job { public HelloQuartzJob() { System.out.println("Hello, Quartz! ----------------------"); } public void execute(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //使用JobDataMap JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); String name = jobDataMap.getString("name"); System.out.println("Hello, " + name + " " + formatter.format(new Date()) + " by " + context.getTrigger()); } }
然后运行测试类:
如果在Job类中定义与JobDataMap中键值一致的set和get方法,那么Quartz会自动将这些属性注入。如:
public class HelloQuartzJob implements Job { String name; public HelloQuartzJob() { System.out.println("Hello, Quartz! ----------------------"); } public void execute(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //使用JobDataMap // JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); // String name = jobDataMap.getString("name"); System.out.println("Hello, " + name + " " + formatter.format(new Date()) + " by " + context.getTrigger()); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
另外Trigger中也可以设置JobDataMap属性,这是为了在多个Trigger中使用相同的Job。JobExecutionContext 将会合并JobDetail与Trigger的JobDataMap,如果其中属性名相同,后者将覆盖前者。可以使用context.getMergedJobDataMap()方法来获取合并后的JobDataMap。
Job的状态与并发
@DisallowConcurrentExecution,如果使用该注解,那么同一时间将只有一个Job实例被执行。如,ReportJob有个实例为ReportForJoe,那么同一时间只有一个ReportForJoe被执行。而ReportForMike等都可以执行。
@PersistJobDataAfterExecution,如果使用该注解,在Job被执行结束后,将会更新JobDataMap,这样下次Job执行后就会使用新的值而不是初始值。
如果使用@PersistJobDataAfterExecution注解,推荐也使用@DisallowConcurrentExecution注解,这是为了避免并发问题导致数据紊乱。
其它属性
- Durability,持久性;如果Job是非持久性的,一旦没有Trigger与其相关联,它就会从Scheduler中被删除。也就是说Job的生命周期和其Trigger是关联的。
- RequestsRecovery,如果为true,那么在Scheduler异常中止或者系统异常关闭后,当Scheduler重启后,Job会被重新执行。
JobExecutionException
execute()方法只允许抛出JobExecutionException异常
Job与JobDetail是Quartz用来定义具体任务的,而Trigger则是用来定义任务如何执行的。Quartz提供了Trigger接口来定义公共属性,使用TriggerBuilder可以创建具体类型的Trigger;最常见的两种Trigger分别是SimpleTrigger、CronTrigger。
Trigger的公共属性:
- key,该属性是为了标识Trigger的。
- startTime,Trigger第一次被Scheduler触发的时间;该属性的值是指定某个时间点的java.util.Date对象。
- endTime,Trigger不再被执行的时间。
- priority,优先级;通过设置优先级属性可以控制Trigger被执行的顺序,该属性默认值是5,可以为正整数也可以为负整数。需要注意的是,只有在触发时间相同时,优先级属性才会有效;比如10:59执行的任务总是会在11:00执行的任务之前执行;另外,如果Trigger是可恢复的,那么恢复后,优先级是不会改变的。
- misfire,如果因为某些原因,错过触发时间,就需要使用该属性来调整。不同类型的Trigger拥有不同的misfire,但是默认的是smart policy,这种情况下会根据Trigger的类型与配置来动态的调整行为。
- Calendars,该属性并不是java.util.Calendar类型,它的作用是排除某些时间,比如在周末不执行任务。Quratz的Calendar对象是一个实现了Calendar接口的序列化对象,Calendars接口如下:
package org.quartz; public interface Calendar { public boolean isTimeIncluded(long timeStamp); public long getNextIncludedTime(long timeStamp); }
这两个方法都是精确到毫秒的;如果只是排除以天为单位的时间,可以使用HolidayCalendar。
Calendars必须实例化,然后通过Scheduler的addCalendar()方法注册。Calendars可以重复使用,如下代码:
Trigger trigger1 = TriggerBuilder.newTrigger() .withIdentity("trigger1", "test") .startAt(DateBuilder.futureDate(3, IntervalUnit.SECOND)) .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(5000) .withIntervalInSeconds(5) .withRepeatCount(5)) .modifiedByCalendar("holidayCalendar") .build(); Trigger trigger2 = TriggerBuilder.newTrigger() .withIdentity("trigger2", "test") .startAt(DateBuilder.futureDate(3, IntervalUnit.SECOND)) .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(5000) .withIntervalInSeconds(5) .withRepeatCount(5)) .modifiedByCalendar("holidayCalendar") .build();
SimpleTrigger
下面来看SimpleTrigger。通过之前的例子可以看到如何创建Trigger,首先通过TriggerBuilder.newTrigger()方法建立一个TriggerBuilder对象,然后通过withSchedule()方法指定了SimpleScheduleBuilder,最后build()方法构建出了SimpleTrigger对象。
SimpleTrigger很简单,之前例子中创建的都是SimpleTrigger,任务启动后每隔5s运行一次,总共运行5次。Quartz提供了DateBuilder工具类来方便设置时间,里面提供了很多方法,如上面Trigger2设置的启动时间就是3秒后启动任务。
CronTrigger
CronTrigger使用cron表达式来设置触发时间。CronTrigger创建方式:
Trigger trigger3 = TriggerBuilder.newTrigger() .withIdentity("cron trigger", "test") .withSchedule( //每5秒执行一次 CronScheduleBuilder.cronSchedule("0/5 * * ? * *") ).build();
cron表达式
cron表达式的格式为:秒 分 时 日 周 年;其中年是可选的,其它为必填。
附:cronExpression配置说明
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
表达式 意义
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
特殊字符 意义
* 表示所有值;
? 表示未说明的值,即不关心它为何值;
- 表示一个指定的范围;
, 表示附加一个可能值;
/ 符号前表示开始时间,符号后表示每次递增的值;
L("last") ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。
W("weekday") 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。
# 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天
下图为每个属性允许使用的符号:
转:http://blog.csdn.net/a4307515