如果是基于日历式配置的调度,而不是某个精确的时间点的话,CronTrigger通常比SimpleTrigger使用得更广泛。
使用CronTrigger,可以指定诸如“每个周五中午”,“每个工作日的9点半”,“二月份每个周一、周三、周五的9点到10点之间每隔五分钟”之类的时间配置。
尽管如此,和SimpleTrigger一样,CronTrigger也有startTime和endTime,强制地约束了任务的开始时间和结束时间。
Cron Expressions
Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions
配置CronTrigger时会用到Cron表达式,它是由7个字符串用空格分开组成的表达式。7个字符串分别代表:
1. 秒
2. 分
3. 时
4. 月中的某天
5. 月
6. 周中的某天
7. 年
例如表达式“0 0 12 ? * WED”代表每个周三的12点。
每个字段都可以是有值范围或者值列表组成,例如“MON-FRI”, “MON,WED,FRI”, 或者“MON-WED,SAT”。
*号在每个字段中都表示这个字段的所有取值,例如月份字段上的*代表每个月。
-表示范围每个字段都有其值得取值范围,显而易见,分和秒的取值范围是0-59,小时是0-23,每周的日是1-31,但是得注意每个月具体有多少天。月份取值在1-11,但是用JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 和 DEC来表示。每月的日取值为1-7(1表示周日),或者使用字符串SUN, MON, TUE, WED, THU, FRI 和 SAT表示。
字符’/可以用来表示字段值递增的差值。例如分钟字段上的’0/15’表示从0分钟开始(包括0分钟)每15分钟都触发执行。’3/20’表示从第3分钟开始,每隔20分钟都触发执行,也就等同于’3,23,43’。特别注意’/35’并不是表示每隔35分钟触发执行,而是等同于’0/35’。
? 表示不指定的随意值,通常用在一组限制中不限制的那个字段。例如配置每个月10号(并不在意是周几)执行的调度,在【Day of month】字段配置10,【Day of week】字段配置? 即可;
- L 表示最后一个,在不同的字段上有不同的含义。例如在【Day of month】上的L表示每个自然月的最后一天;在【Day of week】上如果单独使用,表示周日,但是如果用在另一个值之后,例如6L,则表示每个月的最后一个周六;也可以在L的基础上设置一个偏移量,例如L-3在【Day of month】就表示每个月的倒数第三天。使用L的时候,尽量不用要列举值、范围值,不然会出现意想不到的结果;
- W 表示工作日,表示离指定日期最近的一个工作日。例如“15W”在【Day of month】表示离15号最近的一个工作日,如果15号是工作日,那么就是15号,如果15号是周六,那么15W就会取值14,如果15号是周日,那么15W就会取值16。但这里有一个例外,就是这里的浮动不会跨月,例如1号是周六,那么1W就不会取值上个月的最后一天,而是会在当月取值3号;
- # 表示每个月的第几个周几。例如“6#3”表示第三个周五(注意,周日是每周的第一天,所以6表示周五)。当然如果配置5#,而没有第五个周几,那么调度永远也不会启动的;
这里有一些例子,更多的用法请参考JavaDoc。
Example Cron Expressions
每五分钟触发执行
“0 0/5 * * * ?”
每五分钟,第十秒触发执行
“10 0/5 * * * ?”
每个周三、周五的10:30, 11:30, 12:30, and 13:30触发执行
“0 30 10-13 ? * WED,FRI”
每个月的5号、20号的8:00, 8:30, 9:00触发执行
“0 0/30 8-9 5,20 * ?”
注意,如果表达式写的太复杂,不容易理解的话,可以拆分成多个调度表达式,执行相同的任务即可。
Building CronTriggers
CronTrigger实例通过TriggerBuilder (trigger的通用属性) 和 CronScheduleBuilder (CronTrigger专有属性). 引入方式如下:
import static org.quartz.TriggerBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
Build a trigger that will fire every other minute, between 8am and 5pm, every day:
创建一个trigger,每天上午8点到下午5点,每隔五分钟执行一次:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
.forJob("myJob", "group1")
.build();
Build a trigger that will fire daily at 10:42 am:
创建一个trigger,每天的10:42执行:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(dailyAtHourAndMinute(10, 42))
.forJob(myJobKey)
.build();
或者:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 * * ?"))
.forJob(myJobKey)
.build();
创建一个trigger,每个周三的10:42执行,同时指定时区,而不是系统的默认时区:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))
.forJob(myJobKey)
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.build();
或者:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 ? * WED"))
.inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
.forJob(myJobKey)
.build();
CronTrigger Misfire Instructions
下面介绍下当CronTrigger哑火时,Quartz会如何做。哑火时的策略分为:
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
MISFIRE_INSTRUCTION_DO_NOTHING
MISFIRE_INSTRUCTION_FIRE_NOW
所有trigger都有Trigger.MISFIRE_INSTRUCTION_SMART_POLICY ,而且这是默认的策略。CronTriggers会根据给定CronTriggers实例的配置和状态动态的选择策略。JavaDoc中的CronTrigger.updateAfterMisfire()方法介绍了这个动态选择行为的细节。
在创建CronTriggers时,可以通过CronSchedulerBuilder来指定哑火策略作为调度的一部分。如下所示:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?")
..withMisfireHandlingInstructionFireAndProceed())
.forJob("myJob", "group1")
.build();