Trigger
Trigger是什么?
Quartz中的触发器用来告诉调度程序作业什么时候触发,即Trigger对象是用来触发执行job的。Quartz有两大触发器:SimpleTrigger
和CronTrigger
SimpleTrigger
SimpleTrigger可以满足的调度需求是:在具体的时间点执行一次,或者在具体的时间点执行,并且以指定的间隔重复执行若干次。
SimpleTrigger的属性包括:开始时间、结束时间、重复次数以及重复的间隔。
- 重复次数:可以是
0
、正整数
,以及常量SimpleTrigger.REPEAT_INDEFINITELY
。
REPEAT_INDEFINITELY是-1,也就是-1表示无限次。 - 重复的间隔:必须是
0
,或者long
型的正数
,表示毫秒
。
注意,如果重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。 - endTime:属性的值会
覆盖
设置重复次数
的属性值;
比如,你可以创建一个trigger,在终止时间之前每隔10秒执行一次,你不需要去计算在开始时间和终止时间之间的重复次数,只需要设置终止时间并将重复次数设置为REPEAT_INDEFINITELY(当然,你也可以将重复次数设置为一个很大的值,并保证该值比trigger在终止时间之前实际触发的次数要大即可)。
1.指定时间开始触发,不重复:
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startAt(myStartTime) // some Date
.forJob("job1", "group1") // identify job with name, group strings
.build();
2.指定时间触发,每隔10秒执行一次,重复10次:
SimpleTrigger trigger = (SimpleTrigger) 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();
3.立即触发,每隔5分钟执行一次,直到22:00:
trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever())
.endAt(dateOf(22, 0, 0))
.build();
请查阅TriggerBuilder和SimpleScheduleBuilder提供的方法,以便对上述示例中未提到的选项有所了解。
TriggerBuilder
(以及Quartz的其它builder)会为那些没有被显式设置的属性
选择合理的默认值
。比如:如果你没有调用withIdentity(..)
方法,TriggerBuilder会为trigger生成一个随机
的名称;如果没有调用startAt(..)
方法,则默认使用当前时间
,即trigger立即生效。
CronTrigger
基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更常用。
1.Cron表达式
用于配置CronTrigger实例
是由7个子表达式组成的字符串,描述了时间表的详细信息
格式:[秒][分][小时][日][月][周][年]
书写规则:知道确定日期的写上,不知道的用*代替 (年可以省略,只写前6个
)
序号 | 说明 | 是否必填 | 允许填写的值 | 允许的通配符 |
---|---|---|---|---|
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 小时 | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1-31 | , - * ? / L W |
5 | 月 | 是 | 1-12 or JAN-DEC | , - * / |
6 | 周 | 是 | 1-7 or SUN-SAT | , - * ? / L # |
7 | 年 | 否 | empty 或 1970-2099 | , - * / |
2.Cron表达式特殊字符意义对应表
一个完整的Cron-Expressions的例子是字符串“0 0 12?* WED“ - 这意味着”每个星期三下午12:00“。
单个子表达式可以包含范围和/或列表。例如,可以用“MON-FRI”,“MON,WED,FRI”或甚至“MON-WED,SAT”代替前一个(例如“WED”)示例中的星期几字段。
通配符(’ '字符)可用于说明该字段的“每个”可能的值。因此,前一个例子的“月”字段中的“”字符仅仅是“每个月”。因此,“星期几”字段中的“*”显然意味着“每周的每一天”。
所有字段都有一组可以指定的有效值。这些值应该是相当明显的 - 例如秒和分钟的数字0到59,数小时的值0到23。日期可以是1-31的任何值,但是您需要注意在给定的月份中有多少天!月份可以指定为0到11之间的值,或者使用字符串JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。星期几可以指定为1到7(1 =星期日)之间的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。
-
*
字符:表示所有值。
例如:在分的字段上设置 “*”,*表示每一分钟都会触发。 -
-
字符:表示区间。
例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。 -
,
字符 表示指定多个值。
例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发 -
/
字符:可用于指定值的增量。
例如:
在“分钟”字段中输入“0/15
”,则表示“每隔15分钟,从零开始
”。
在“分钟”字段中使用“3/20
”,则意味着“每隔20分钟,从三分钟开始
” , 换句话说,等价于“分钟”字段设置为“3,23,43
”。
在日字段上设置’1/3
'所示每月1号开始,每隔三天触发一次。请注意“
/ 35
”的细微之处并不代表“每35分钟” - 这意味着“每隔35分钟,从零开始” - 或者换句话说,与指定“0,35
”相同。 -
?
字符:表示不指定值。
仅用在日
和星期
字段,不需要关心当前设置这个字段的值,表示一个月的每一天或一周的每天。
例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ? -
L
字符:last 允许用于日
和星期
字段,表示最后
的意思。
例如:
1) “日
”字段中的“L”表示“月的最后一天
” - 1月31日,非闰年2月28日。
2) “周
”字段上表示星期六
(西方把星期天当前每周的第一天,周六是最后一天),相当于"7
“或”SAT
"。
3)如果在星期的字段中再次使用这个值,就意味着“该月最后一个xxx日”,例如“6L”或“FRIL”都意味着“该月的最后一个星期五”。
4) 您还可以指定与该月最后一天的偏移量
,例如“ L-3”,这表示日历月的倒数第三天。 使用“ L”选项时,不要指定列表或值的范围,这一点很重要,因为这样会导致混淆/意外的结果 -
W
字符: workday 用于指定最近给定日期的工作日(星期一至星期五)。
例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,“W"前只能设置具体的数字,不允许区间”-").'L’和 'W’可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资 )
-
#
序号(表示每月的第几个周几)。
例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)
周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同.
3. '*‘与’?'的区别
二者意思相近,容易让人理解成可以任意替换,实际上二者是有区别的,存在如下的隐藏的约束关系:
1、如官方文档解释的那样,问号(?
)的作用是指明该字段‘没有特定的值
’;
2、星号(*
)和其它值,比如数字,都是给该字段指明特定的值
,只不过用星号(*)代表所有可能值;
3、cronExpression对日期和星期字段的处理规则是它们必须互斥
,即只能且必须有一个字段有特定的值
,另一个字段必须是‘没有特定的值’
;
4、问号(?
)就是用来对日期和星期字段做互斥
的。
因此存在如下的错误场景:
1、当星期和日期都为*或数字时,报错 Support for specifying both a day-of-week AND a day-of-month parameter is not implemented
.
即两个字段不能都指明的特定的值,必须互斥。这里的*和数字是一样的,如果都指明特定的数字,也是报一样的错。
2、 当星期和日期有一个为数字另一个为*时,报错:
Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.
原因同一,因为*是数字的变种,本质上也是特定的数字。
3、当星期和日期都为?时,报错 :
'?' can only be specfied for Day-of-Month -OR- Day-of-Week.
以下是一些表达式及其含义的更多示例 - 您可以在JavaDoc中找到更多的org.quartz.CronExpression
Cron Expressions示例
CronTrigger示例1 - 创建一个触发器的表达式,每5分钟就会触发一次
“0 0/5 * * *?”
CronTrigger示例2 - 创建触发器的表达式,每5分钟触发一次,分钟后10秒(即上午10:00:10,上午10:05:10等)。
“10 0/5 * * *?”
CronTrigger示例3 - 在每个星期三和星期五的10:30,11:30,12:30和13:30创建触发器的表达式。
“0 30 10-13?* WED,FRI“
CronTrigger示例4 - 创建触发器的表达式,每个月5日和20日上午8点至10点之间每半小时触发一次。请注意,触发器将不会在上午10点开始,仅在8:00,8:30,9:00和9:30
“0 0/30 8-9 5,20 *?”
请注意:
1.一些调度要求太复杂,无法用单一触发表示 - 例如“每上午9:00至10:00之间每5分钟,下午1:00至晚上10点之间每20分钟”一次。在这种情况下的解决方案是简单地创建两个触发器,并注册它们来运行相同的作业。
配置Maven依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
完整例子
HelloJob.java,共用
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("HelloJob...." + new Date());
}
}
1.SimpleTrigger例子
import java.util.concurrent.TimeUnit;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class Test {
public static void main(String[] args) throws InterruptedException {
Scheduler scheduler;
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
System.out.println("quartz start....");
scheduler.start();
/********************** doWork() start **************/
// 每五秒执行一次
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1")
.build();
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5).repeatForever();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1")
.startNow().withSchedule(simpleScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
2.CronTrigger例子
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Scheduler scheduler;
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
System.out.println("quartz start....");
scheduler.start();
/********************** doWork() start **************/
// 每秒执行一次
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "jobGroup1")
.build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow().withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
.build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
总结
无论哪种trigger,最终都要调用
scheduler.scheduleJob(jobDetail, trigger);
因此如果想搜索工程中用到的所有定时任务,可以搜索scheduleJob