Quartz Tutorial 5 - Cron Trigger

Quartz Tutorial 5 - Cron Trigger

Quartz Tutorial 5 - Cron Trigger

当你要求作业的调度方式是“日历式”而不是“给定时间间隔式”的时候,CronTriggerSimpleTrigger好用多了。

CronTrigger的话,很容易就能声明类似“每周五下午”或者“每周工作日上午9:30”,甚至“一月份的每个周一周三周五的上午九点到十点之间,每五分钟”这种奇奇怪怪的触发时间。

此外,CronTrigger也有个startTime属性用于声明何时强制调度,也有一个(可选的)endTime属性用于声明何时结束调度。

Cron Expressions

Cron表达式是用于配置CronTrigger实例的。Cron表达式是由六个子表达式组成的字符串,它们互相独立地描述调度的各个细节。这些子表达式是由空格分割,它们表示:

  1. 秒(Seconds)
  2. 分钟(Minutes)
  3. 小时(Hours)
  4. 月份中的天(Day-of-Month)
  5. 月份(Month)
  6. 一周中的天(Day-of-Week)
  7. 年(可选)(Year)

举个例子,假定有字符串"0 0 12 ? * WED",它表示“每周三的中午12点”

这些互相独立的子表达式可以包含范围和/或列表。举个例子,上面例子中的“一周中的天”字段可以替换成MON-FRIMON,WED,FRI甚至可以是MON-WED,SAT

Cron表达式支持使用通配符。上例中,“月份中的天”表达式处的?表示每个“可能”的值,换言之,这个值是不确定的(但不是全部)。而对于*,就表示了所有值都是。

每个子表达式都有其自己的定义域。这也很显而易见了-对于秒和分钟,肯定是0到59;对于小时也必然是0到23;对于月份中的天,定义域是1到31,但是你自己应当注意这个月实际的天数;月份可以是数字0到11,也可以是字符串“JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC”;一周中的天可以是数字1到7(1=Sunday),也可以是字符串“SUN MON TUE WED THU FRI SAT

/ 用于声明值的增量。例如,分钟子表达式是0/15,它的意思是“在小时内,0分钟之后的每个第15分钟触发一次”;如果是3/20,意味着“小时内,3分钟之后的每个第20分钟触发一次”,换一个写法,分钟处的表达式就是3 23 43。要注意到一个细微的区别:/35不是说“每个35分”,而是说“从0分钟开始后的每个第35分钟”,换个写法就是0 35

? 可以用在月份中的天和一周中的天子表达式中。这个符号表示“非确定的值”。当你需要明确声明其中一个值,但是另一个无法明确指定的时候,这很有用。比如你想在每个星期四触发作业的执行,但是周四具体是几号其实是不确定的,那么此时在月份中的天子表达式处就可以写这个通配符了。

L 可以用在月份中的天和一周中的天子表达式处。这是”last”的简写,但它在两个子表达式中具有不同的意义。例如,在月份中的天子表达式处填写L,意味着“月份中的最后一天”,例如一月份的31号,平年2月的28号等等;如果单独的L放在一周中的天子表达式处,意味着7或者SAT(周六);但是在一周中的天子表达式中,如果L放在了其他值的后面,例如6LFRIL,那就意味着“该月的最后一个周五”。你也可以声明相对于每月最后一天的偏移,例如L-3意味着该月的倒数第三天。当你使用L时,不要再声明列表式、范围式的值了,不然你将得到奇怪的结果

W 用于声明距离给定日子最近的工作日(周一至周五)。例如你在月份中的天子表达式中声明了15W,那意味着该月“距离15号最近的工作日”

# 用于声明“月份中第X个周X”。例如在一周中的天子表达式处给出6#3或者FRI#3,意思是“该月第三个周五”

下面是一些Cron表达式及其含义的具体举例,当然你可以在org.quartz.CronExpression的JavaDoc中找到更多。

Example Cron Expressions

  • 每五分钟触发一次

    1
    
    0 0/5 * * * ?
    
  • 每五分钟的第十秒触发,例如“10:00:10, 10:05:10”

    1
    
    10 0/5 * * * ?
    
  • 每周三、周五的10:30, 11:30, 12:30, 13:30触发

1
0 30 10-13 * * WED,FRI
  • 每月5号和20号的上午8点到10之间,每半小时触发一次。要注意10点并不会触发,只是八点、八点半、九点、九点半

    1
    
    0 0/30 8-9 5,20 * ?
    

有时有些调度的要求特别复杂,如果放在一个表达式中表示会过于复杂,例如“上午九点到十点之间每五分钟,下午一点到十点之间每二十分钟”。这种情况的话,可以创建两个触发器,然后将他俩都关联到同一个作业就好。

Building CronTriggers

CronTrigger的实例构造是通过TriggerBuilder类(设置触发器的主要共有属性)和CronScheduleBuilder类(设置CronTrigger类型触发器特有的属性)。首先静态导入如下内容,就能用DSL风格构造触发器实例了。

1
2
3
import static org.quartz.TriggerBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.DateBuilder.*;
  • 每天上午的八点到下午五点,每两分钟触发一次

    1
    2
    3
    4
    5
    
    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(cronSchedule("0 0/2 8-17 * * ?"))
        .forJob("myJob", "group1")
        .build();
    
  • 每天上午10点42分触发

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(dailyAtHouAndMinute(10, 42))
        .forJob(myJobKey)
        .build()
    // or
    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(cronSchedule("0 42 10 * * ?"))
        .forJob(myJobKey)
        .build();
    
  • 在周三上午10点42触发,此外,时区与系统默认时区不同

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(weeklyOnDayAdnHourAdnMinute(DateBuilder.WEDNESDAY, 10, 42))
        .forJob(myJobKey)
        .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
        .build();
    // or
    trigger = newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(cronSchedule("0 42 10 * * WED"))
        .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))
        .forJob(myJobKey)
        .build();
    

CronTrigger Misfire Instruction

当发生了misfire(“哑火”?),CronTrigger提供了一些指令用于告知Quartz该如何处理。这些指令是作为CronTrigger自身的常量来定义的(JavaDoc介绍了它们的行为),这些常量包括:

1
2
3
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
MISFIRE_INSTRUCTION_DO_NOTHING
MISFIRE_INSTRUCTION_FIRE_NOW

所有类型的触发器都有Trigger.MISFIRE_INSTRUCTION_SMART_POLICY指令可用,这也是所有触发器默认的选择。在CronTrigger中,该“smart policy”会使用MISFIRE_INSTRUCTION_FIRE_NOWCronTrigger.updateAfterMisfire()的JavaDoc介绍了具体的行为细节。

当构造CronTrigger的实例时,你也可以声明该属性(通过CronScheduleBuilder

1
2
3
4
5
6
trigger = newTrigger()
    .withIdentity("trigger3", "group1")
    .withSchedule(cronSchedule("0 0/2 8-17 * * ?")
        .withMisfireHandlingInstructionFireAndProceed())
    .forJob("myJob", "group1")
    .build()