参考地址:https://www.cnblogs.com/mengrennwpu/p/7142124.html
Trigger负责设置调度策略。该类是一个接口,描述触发 job 执行的时间触发规则。主要有 SimpleTrigger 和 CronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 15:00~16:00 执行调度等。
1. 通用Trigger属性
quartz中所有的触发器Trigger都有一些共有属性,如TriggerKey,startTime等,这些属性可以使用TriggerBuilder进行设置。常用的属性举例如下:
(1) triggerKey:触发器的标识,由名称与分组唯一指定,便于调度器调用与查找。
(2) jobKey: 当触发器被触发时,标识哪一个任务Job应该被执行。
(3) startTime: 表示触发器第一次开始触发的时间。
(4) endTime: 表示触发器终止触发的时间。
2. 优先级
当存在多个触发器时,quartz可能没有足够的资源立即触发所有配置为同一时间触发的triggers,因此可以设置每个Trigger的优先级。默认的优先级为5,可取任意的整型值,包括正数或负数。
注意:只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。
注意:如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。
3. 未启动指令"Misfire Instructions"
Trigger未触发一般产生于调度器被关闭,或线程池不足时。不同的Trigger类型有不同的未启动指令。默认的,他们会使用"智能机制smart policy",这些指令的使用场景在于,当scheduler开启时,它将搜索所有未启动的持久化的触发器,然后基于触发器各自配置"未启动指令"来更新触发器。未启动指令用于当trigger未正常触发时,是否恢复执行的场景。
4. Calendars
与Trigger关联的Calendar对象,用于在指定的时间内不触发trigger,例如你有一个任务每天执行一次,但你不希望在节假日执行时,Calendar此时派上用场。注意此Calendar为quartz自身的定义接口,而非Java自带的Calendar。
Calendar需要在Scheduler定义过程中,通过scheduler.addCalendar()进行初始化和注册。
任何实现了Calendar接口的可序列化对象都可以作为Calendar对象,Calendar接口如下:
package org.quartz;
public interface Calendar {
public boolean isTimeIncluded(long timeStamp);
public long getNextIncludedTime(long timeStamp);
}
注意到这些方法的参数类型为long。你也许猜到了,他们就是毫秒单位的时间戳。即Calendar排除时间段的单位可以精确到毫秒。你也许对“排除一整天”的Calendar比较感兴趣。Quartz提供的org.quartz.impl.HolidayCalendar类可以很方便地实现。
Calendar必须先实例化,然后通过addCalendar()方法注册到scheduler。如果使用HolidayCalendar,实例化后,需要调用addExcludedDate(Date date)方法从调度计划中排除时间段。以下示例是将同一个Calendar实例用于多个trigger:
HolidayCalendar cal = new HolidayCalendar();
cal.addExcludedDate( someDate );
cal.addExcludedDate( someOtherDate );
sched.addCalendar("myHolidays", cal, false);
Trigger t = TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.forJob("myJob")
.withSchedule(dailyAtHourAndMinute(9, 30)) // execute job daily at 9:30
.modifiedByCalendar("myHolidays") // but not on holidays
.build();
// .. schedule job with trigger
Trigger t2 = TriggerBuilder.newTrigger()
.withIdentity("myTrigger2")
.forJob("myJob2")
.withSchedule(dailyAtHourAndMinute(11, 30)) // execute job daily at 11:30
.modifiedByCalendar("myHolidays") // but not on holidays
.build();
// .. schedule job with trigger2
上面的代码创建了两个触发器,每个触发器都计划每天触发。然而,在日历所排除的期间内发生的任何发射都将被跳过。
5. SimpleTrigger
当需要在规定时间执行一次或在规定的时间段以一定的时间间隔重复触发执行Job时,SimpleTrigger就可以满足。
SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔。重复次数属性的值可以为0、正整数、或常量 SimpleTrigger.REPEAT_INDEFINITELY,重复的时间间隔属性值必须为0或长整型的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行。如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每间隔10秒钟触发一次直到指定的结束时间的 Trigger,而无需去计算从开始到结束的所重复的次数,我们只需简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性值即可。
5.1 创建在特定时间触发,非重复的Trigger
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.DateBuilder.*;
SimpleTrigger trigger =TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startAt(myStartTime) // some Date
.forJob("job1", "group1") // identify job with name, group strings
.build();
5.2 创建在特定时间触发,重复间隔为10次,重复执行10次的Trigger
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
.forJob(myJob) // identify job with handle to its JobDetail itself
.build();
5.3 创建5分钟后执行,且触发一次的Trigger
trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger5", "group1")
.startAt(futureDate(5, IntervalUnit.MINUTE)) // use DateBuilder to create a date in the future
.forJob(myJobKey) // identify job with its JobKey
.build();
5.4 创建立即执行,且重复间隔为5分钟,到22点结束的Trigger
trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)//间隔时间为5分钟
.repeatForever())
.endAt(dateOf(22, 0, 0))//在晚上十点结束
.build();
5.5 创建在下一小时触发,且每2小时执行重复执行的Trigger
trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
.startAt(evenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))
.withSchedule(simpleSchedule()
.withIntervalInHours(2)
.repeatForever())
// note that in this example, 'forJob(..)' is not called
// - which is valid if the trigger is passed to the scheduler along with the job
.build();
scheduler.scheduleJob(trigger, job);
5.6 包SimpleTrigger的未启动指令含如下:
//该策略也是所有trigger的默认策略。如果使用smart policy,SimpleTrigger会根据实例的配置及状态,在所有MISFIRE策略中动态选择一种Misfire策略。
MISFIRE_INSTRUCTION_SMART_POLICY
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
MISFIRE_INSTRUCTION_FIRE_NOW
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
构件SimpleTrigger的时候,可以指定Trigger的未启动指令:
trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever()
.withMisfireHandlingInstructionNextWithExistingCount())
.build();
6. CronTrigger
6.1 cron表达式
CronTrigger 支持比SimpleTrigger更具体、更复杂的调度。基于cron表达式,CronTrigger支持类似日历的重复间隔,而非单一的时间间隔。
序号 | 说明 | 是否必填 | 允许填写的值 | 允许的通配符 |
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 小时 | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1-31 | , - * ? / L W |
5 | 月 | 是 | 1-12或JAN-DEC | , - * / |
6 | 周 | 是 | 1-7或SUN-SAT | , - * ? / L # |
7 | 年 | 否 | 空或1999-2017 | , - * / |
通配符的说明:
(1) 反斜线(/)字符表示增量值。例如,在秒字段中“5/15”代表从第 5 秒开始,每 15 秒一次。
(2) 星号(*)字符是通配字符,表示该字段可以接受任何可能的值,例如:在分的字段上设置 "*",表示每一分钟都会触发。
(3) 问号(?)问号表示这个字段不包含具体值。如果指定月内日期,可以在月内日期字段中插入“?”,表示周内日期值无关紧要。
(4) - 表示区间,例如 在小时上设置 "10-12",表示 10,11,12点都会触发。
(5) 逗号(, ) 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发。
(6) 井号(#)字符为给定月份指定具体的工作日实例。把“MON#2”放在周内日期字段中,表示把任务安排在当月的第二个星期一。
(7) L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"。
(8) W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-")。
注:'L'和 'W'可以组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发。
cron在线生成网址:http://cron.qqe2.com/
6.2 CronTrigger构建
可以使用TriggerBuilder 来定义CronTrigger对象。示例如下:
import static org.quartz.TriggerBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.DateBuilder.*:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
.forJob("myJob", "group1")
.build();
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 = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))
.forJob(myJobKey)
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))//指定时区
.build();
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 42 10 ? * WED"))
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.forJob(myJobKey)
.build();
6.3 CronTrigger的未启动指令
未启动指令的值:
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
MISFIRE_INSTRUCTION_DO_NOTHING
MISFIRE_INSTRUCTION_FIRE_NOW
MISFIRE_INSTRUCTION_SMART_POLICY
未启动指令的使用:
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(cronSchedule("0 0/2 8-17 * * ?")
.withMisfireHandlingInstructionFireAndProceed())
.forJob("myJob", "group1")
.build();
7、为什么既有Job又有Trigger
为什么既有Job,又有Trigger呢?很多任务调度器并不区分Job和Trigger。有些调度器只是简单地通过一个执行时间和一些job标识符来定义一个Job;其它的一些调度器将Quartz的Job和Trigger对象合二为一。在开发Quartz的时候,我们认为将调度和要调度的任务分离是合理的。在我们看来,这可以带来很多好处。
例如,Job被创建后,可以保存在Scheduler中,与Trigger是独立的,同一个Job可以有多个Trigger;这种松耦合的另一个好处是,当与Scheduler中的Job关联的trigger都过期时,可以配置Job稍后被重新调度,而不用重新定义Job;还有,可以修改或者替换Trigger,而不用重新定义与之关联的Job。