综述
本篇介绍Trigger, 它是顶层接口,Trigger的类结构如下:
MutableTrigger:是状态可变的触发器,不单独使用,所有Trigger都继承此接口。
CoreTrigger:存在额外属性的触发器,只有一个方法hasAddtionalProperties。不单独使用,所有Trigger都继承此接口。
CalendarIntervalTrigger:根据日期,间隔触发,日期中只包含年,月,日,间隔的单位也只有年,月,日。
DailyTimeIntervalTrigger: 根据日期,间隔触发,日期中只包含时,分,秒,间隔的单位也只有时,分,秒。
SimpleTrigger:根据日期,间隔触发,日期中包含年,月,日,时,分,秒。
CronTrigger:根据Cron表达式触发。
经常使用的只有两种,SimpleTrigger和CronTrigger。
除上述对象之外,Trigger相关的对象与Job相关的对象相似,也有TriggerBuilder,TriggerKey,TriggerDataMap, TriggerListener。
属性
@SuppressWarnings("unchecked")
public T build() {
if(scheduleBuilder == null)
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
MutableTrigger trig = scheduleBuilder.build();
trig.setCalendarName(calendarName);
trig.setDescription(description);
trig.setStartTime(startTime);
trig.setEndTime(endTime);
if(key == null)
key = new TriggerKey(Key.createUniqueName(null), null);
trig.setKey(key);
if(jobKey != null)
trig.setJobKey(jobKey);
trig.setPriority(priority);
if(!jobDataMap.isEmpty())
trig.setJobDataMap(jobDataMap);
return (T) trig;
}
calendarName:Calendar对象的唯一标识。排除特定时间,参考Calendar对象。
description:Trigger的描述信息。
startTime:Trigger触发的开始时间。它不是任务真正的开始时间。
endTime:Trigger触发的结束时间。它不是任务真正的结束时间。
triggerKey:Trigger的唯一标识,名称和组。
triggerDataMap:与JobDataMap基本相同。
priority:Trigger的优先级。
SimpleTrigger
SimpleTrigger的知识点有两个,设置SimpleTrigger的属性,它的Mis_fire策略。
2.1 设置属性
设置属性是通过调用TriggerBuilder的相关方法。
TriggerKey:调用withIdentity方法,第一个参数表示name,第二个参数表示group。
startTime:调用startAt方法,参数为date。也可以调用startNow。
间隔:调用withScheduler方法,参数为ScheduleBuilder,它有实现类XXScheduleBuilder,每个实现类中都有withIntervalXXX,后面的XXX代表的是间隔的时间单位,方法的参数代表间隔的时间值。
重复次数:调用withRepeatCount方法,参数为其值。调用repeatForever无限重复。
endTime:调用endAt方法。
关联Job:调用forJob方法。
2.2 MisFire
当SimpleTrigger未被正常触发之后,有以下六种策略。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY: 快速的执行任务(重复次数相同),忽略开始时间,间隔,结束时间。不能用于无限重复的触发器。
/**
* Instructs the <code>{@link Scheduler}</code> that the
* <code>Trigger</code> will never be evaluated for a misfire situation,
* and that the scheduler will simply try to fire it as soon as it can,
* and then update the Trigger as if it had fired at the proper time.
*
* <p>NOTE: if a trigger uses this instruction, and it has missed
* several of its scheduled firings, then several rapid firings may occur
* as the trigger attempt to catch back up to where it would have been.
* For example, a SimpleTrigger that fires every 15 seconds which has
* misfired for 5 minutes will fire 20 times once it gets the chance to
* fire.</p>
*/
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;
MISFIRE_INSTRUCTION_FIRE_NOW:在now执行一次,之后将now作为开始时间,间隔不变,重复次数为剩余的重复次数
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link SimpleTrigger}</code> wants to be fired
* now by <code>Scheduler</code>.
* </p>
*
* <p>
* <i>NOTE:</i> This instruction should typically only be used for
* 'one-shot' (non-repeating) Triggers. If it is used on a trigger with a
* repeat count > 0 then it is equivalent to the instruction <code>{@link #MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT}
* </code>.
* </p>
*/
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT:将now作为开始时间,间隔不变,重复次数为剩余的重复次数,它受结束时间的约束,当到达结束时间时,任务不会在执行,当now大于结束时间时,任务不会执行
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link SimpleTrigger}</code> wants to be
* re-scheduled to 'now' (even if the associated <code>{@link Calendar}</code>
* excludes 'now') with the repeat count left as-is. This does obey the
* <code>Trigger</code> end-time however, so if 'now' is after the
* end-time the <code>Trigger</code> will not fire again.
* </p>
*
* <p>
* <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
* the start-time and repeat-count that it was originally setup with (this
* is only an issue if you for some reason wanted to be able to tell what
* the original values were at some later time).
* </p>
*/
public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = 2;
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT:将now作为开始时间,间隔不变,重复次数为剩余的重复次数,与EXISTING的区别在于它不受结束时间的约束
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link SimpleTrigger}</code> wants to be
* re-scheduled to 'now' (even if the associated <code>{@link Calendar}</code>
* excludes 'now') with the repeat count set to what it would be, if it had
* not missed any firings. This does obey the <code>Trigger</code> end-time
* however, so if 'now' is after the end-time the <code>Trigger</code> will
* not fire again.
* </p>
*
* <p>
* <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
* the start-time and repeat-count that it was originally setup with.
* Instead, the repeat count on the trigger will be changed to whatever
* the remaining repeat count is (this is only an issue if you for some
* reason wanted to be able to tell what the original values were at some
* later time).
* </p>
*
* <p>
* <i>NOTE:</i> This instruction could cause the <code>Trigger</code>
* to go to the 'COMPLETE' state after firing 'now', if all the
* repeat-fire-times where missed.
* </p>
*/
public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = 3;
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT:与NOW的区别是它不改变开始时间,根据原始任务的开始时间,间隔计算下次的触发时间。不受结束时间的约束
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link SimpleTrigger}</code> wants to be
* re-scheduled to the next scheduled time after 'now' - taking into
* account any associated <code>{@link Calendar}</code>, and with the
* repeat count set to what it would be, if it had not missed any firings.
* </p>
*
* <p>
* <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
* to go directly to the 'COMPLETE' state if all fire-times where missed.
* </p>
*/
public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = 4;
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT:与NOW的区别是它不改变触发时间,与REMAINING的区别是它受到时间状态的约束
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link SimpleTrigger}</code> wants to be
* re-scheduled to the next scheduled time after 'now' - taking into
* account any associated <code>{@link Calendar}</code>, and with the
* repeat count left unchanged.
* </p>
*
* <p>
* <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
* to go directly to the 'COMPLETE' state if the end-time of the trigger
* has arrived.
* </p>
*/
public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = 5;
CronTrigger
CronTrigger的知识点有三个,设置属性,Cron表达式,MisFire策略。
3.1 设置属性
triggerKey:调用withIdentity方法,第一个参数为名称,第二个参数为组。
cron表达式:调用withScheduler方法,参数为ScheduleBuilder,CronScheduleBuilder为其实现类之一。调用CronScheduleBuilder的cronSchedule方法,参数为cron表达式。
timezone:调用withScheduler方法,参数为ScheduleBuilder,CronScheduleBuilder为其实现类之一。调用CronScheduleBuilder的inTimeZone方法,参数为TimeZone对象。
3.2 Cron表达式
Cron表达式的格式有七部分组成。分别是:
秒 分钟 小时 天(某月的一天) 月 周 年
下面具体分析每个部分的格式
在使用Cron表达式时,需要注意间隔的时间单位会根据表达式推算出来,例如0/15设置在秒字段,会导致一分钟执行四次任务。
3.2.1 秒
格式: num ,— * /
允许值:0-59
是否必填:是
示例:
单个数值形式,例如0 表示在第0秒执行
多个数值形式(逗号),例如0,15,20 表示在第0秒,15秒,20秒执行
数值范围形式(横杠),例如0-15 表示在0-15秒之间每一秒都执行一次
数值递增形式(斜杠),例如3/15 其中3表示初始值,15表示递增值,表示在3,18, 43 ,58秒执行
任意值(通配符),例如 * 表示每秒执行一次,这个通常不适用
3.2.2 分钟
格式: num ,— * /
允许值:0-59
是否必填:是
示例:
单个数值形式,例如0 表示在第0分钟执行
多个数值形式(逗号),例如0,15,20 表示在第0分,15分,20分执行
数值范围形式(横杠),例如0-15 表示在0-15分之间每一分都执行一次
数值递增形式(斜杠),例如3/15 其中3表示初始值,15表示递增值,表示在3,18, 43 ,58分执行
任意值(通配符),例如 * 表示每分执行一次,这个通常不适用
3.2.3 小时
格式: num ,— * /
允许值:0-24
是否必填:是
示例:
单个数值形式,例如0,表示在第0小时执行
多个数值形式(逗号),例如0,15,20 表示在第0,15分,20分执行
数值范围形式(横杠),例如0-15 表示在0-15分之间每一分都执行一次
数值递增形式(斜杠),例如3/15 其中3表示初始值,15表示递增值,表示在3,18小时执行
任意值(通配符),例如 * 表示每小时执行一次,这个通常不适用
3.2.4 天
格式: num ,— * / ? L W
允许值:1-31
是否必填:是
示例:
单个数值形式,例如1 表示在每月的第一天执行
多个数值形式(逗号),例如1,15,20 表示在第1号,15号,20号执行
数值范围形式(横杠),例如0-15 表示在0-15时之间每一天执行一次
数值递增形式(斜杠),例如3/15 其中3表示初始值,15表示递增值,表示在3,18,号执行
任意值(通配符),例如 * 表示每天执行一次
无特定值(问号),它与通配符的区别在于前者表示任意值,本身为当前字段设置了规则,而无特定值的含义是指本身没有为当前字段设置任何规则,而是由其他字段指定规则,与day-of-month存在交集的就是day-of-week字段。问号的意思是由另外一方指定规则。
L,它有三种形式:
当只有L时,表示当前月的最后一天
当为num L时,num的范围为1-7,表示星期一到星期日,例如3L,表示最后一个星期的星期三。
当L-num时,表示当前月的最后几天,例如L-2,表示当月的最后两天。
W,英文全称为WeekDay,表示工作日,即星期一到星期五。
它表示最近的工作日,格式为num L。例如2W,表示离当月2号最近的一个工作日,总共有以下三种情形
当月2号为星期六,则会在1号星期五执行
当月2号为星期日,则会在3号星期一执行
当月2号为工作日,则会在2号的工作日执行。
不论是哪种形式,不会跨月,例如1号为星期六,只会在3号星期一执行
3.2.5 月
格式: num ,— * /
允许值:1-12
是否必填:是
示例:
单个数值/缩写形式,例如1表示在第1月执行,JAN的含义相同
多个数值/缩写形式(逗号),例如1,3,5 表示在第1,3,5月执行
数值/缩写范围形式(横杠),例如1-13 表示在1-3月之间执行
数值/缩写递增形式(斜杠),例如1/5 其中1表示初始值,5表示递增值,表示在1,6, 11月执行
任意值(通配符),例如 * 表示每月执行一次
3.2.6 周
格式: num ,— * / ? L #
允许值:1-7
是否必填:否
示例:
单个数值/缩写形式,例如1 表示在星期一执行
多个数值/缩写形式(逗号),例如1,4 表示在周一,周四执行
数值/缩写范围形式(横杠),例如1-3 表示在周一,周二,周三执行
数值/缩写递增形式(斜杠),例如3/2 其中3表示初始值,2表示递增值,表示在周三,周六执行
任意值(通配符),例如 * 表示每周执行一次
无特定值(问号),它与通配符的区别在于前者表示任意值,本身为当前字段设置了规则,而无特定值的含义是指本身没有为当前字段设置任何规则,而是由其他字段指定规则,与day-of-week存在交集的就是day-of-month字段。问号的意思是由另外一方指定规则。
L,它有三种含义,与day-of-month的含义相同
当只有L时,表示当前月的最后一天
当为num L时,num的范围为1-7,表示星期一到星期日,例如3L,表示最后一个星期的星期三。
当L-num时,表示当前月的最后几天,例如L-2,表示当月的最后两天。
注:它的周期是月,不是周
#,它的格式为num1 # num2,num1的值为1-7,表示周一到周日,num2的值为当月的第几周,一般不会超过5。例如 2#3,表示当月的第三周的周二。当计算得出无实际日期时,不会出错但也无任何含义,例如5#6,当月第六周的周五无任何含义。
3.2.7 年
格式: num ,— * /
允许值:Empty,1970-2099
是否必填:否
示例:
单个数值形式,例如2020 表示在2020年执行
多个数值形式(逗号),例如2020,2025 表示在2020年和2025年执行
数值范围形式(横杠),例如2020-2025 表示在2020-2025之间执行
数值递增形式(斜杠),例如2020/10 其中2020表示初始值,10表示递增值,表示在2020,2030, 2040等年份执行
任意值(通配符),例如 * 表示每年执行
3.3 MisFire策略
当CronTrigger未被正常触发之后,有以下三种策略。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:短时间内快速的执行任务。与SimpleTrigger的第一种策略相同。不适用于无限重复的任务
/**
* Instructs the <code>{@link Scheduler}</code> that the
* <code>Trigger</code> will never be evaluated for a misfire situation,
* and that the scheduler will simply try to fire it as soon as it can,
* and then update the Trigger as if it had fired at the proper time.
*
* <p>NOTE: if a trigger uses this instruction, and it has missed
* several of its scheduled firings, then several rapid firings may occur
* as the trigger attempt to catch back up to where it would have been.
* For example, a SimpleTrigger that fires every 15 seconds which has
* misfired for 5 minutes will fire 20 times once it gets the chance to
* fire.</p>
*/
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;
MISFIRE_INSTRUCTION_DO_NOTHING:未被触发的被忽略,之后的计算下次执行时间。
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link CronTrigger}</code> wants to have it's
* next-fire-time updated to the next time in the schedule after the
* current time (taking into account any associated <code>{@link Calendar}</code>,
* but it does not want to be fired now.
* </p>
*/
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
MISFIRE_INSTRUCTION_FIRE_NOW:在now执行一次,之后计算下次的执行时间,等价于MISFIRE_INSTRUCTION_SMART_POLICY。
/**
* <p>
* Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
* situation, the <code>{@link CronTrigger}</code> wants to be fired now
* by <code>Scheduler</code>.
* </p>
*/
public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
TriggerListener
除TriggerListener之外,TriggerBuilder, TriggerKey, TriggerDataMap与Job中的相关对象含义基本相同,不再重复。
TriggerListener的源码如下:
public interface TriggerListener {
public String getName();
public void triggerFired(Trigger trigger, JobExecutionContext context);
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
public void triggerMisfired(Trigger trigger);
public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode);
}
getName:返回Trigger的名称
triggerFired:被触发时执行
vetoJobExectution:对Job进行vote,返回true,否决Job,Job将不会再执行。默认返回false。
triggerMisFired:未被正常触发时执行
triggerComplete:触发完成时执行
Listener的触发顺序分两种情况:
当Job被否决了。顺序如下:
- 调用TriggerListener的triggerFired方法
- 调用TriggerListener的vetoJobExecution方法
- 调用JobListener的jobExecutionVetoed方法
当Job未被否决时,顺序如下:
- 调用TriggerListener的triggerFired方法
- 调用TriggerListener的vetoJobExecution方法
- 调用JobListener的jobToBeExecuted方法
- 调用JobListener的jobWasExecuted方法
- 调用TriggerListener的triggerComplete方法。