初识 cron

cron 表达式是一个字符串,分为 6 或 7 个域,每个域都会代表一个含义。

语法格式
6 个域:second minute hour day month week
7 个域:second minute hour day month week year
由上可见,7 个域与 6 个域的语法只差了 year,一般情况下,我们使用 6 个域的结构。

cron表达式结构

cron 从左到右(中间用空格隔开):秒 分 小时 月份中的日 月份 星期中的日期 年份

各个字段的含义

位置 时间域名 允许值 允许的特殊字符
1 0-59 ,   -   *   /
2 0-59 ,   -   *   /
3 小时 0-23 ,   -   *   /
4 1-31 ,   -   *   ?   /   L   W  C
5 0-12 ,   -   *   /  
6 星期 1-7 ,   -   *   ?  /   L   C   #
7 年(可选) 1970-2099 ,   -   *   /

代码示例

声明:本次的 cron 讲解结合了 springboot 框架。由于只是讲解 cron 表达式,所以就不在介绍 springboot 中如何使用 @Scheduled 注解的相关配置了
秒的位置允许值为 0-59,如果写 60,将会报错

/* 以下代码在 springboot 启动时会抛出异常 */
@Scheduled(cron="60 * * * * * ?")
public void executeSchedule() {
    //TODO
}

异常信息:Range exceeds maximum (60) |'60' in expression "60 ?"
意思就是说,表达式中的 60 已经超出了最大范围。

/* 每分钟的第8秒执行 */
@Scheduled(cron="8 * * * * ?")
public void executeSchedule() {
    //TODO
}

以上表达式含义为:每分钟的第 8 秒执行 executeSchedule方法。秒后面的时间域是分钟,*(星号)表示每...,这里表示每分钟
注意:是每分钟的第 8 秒,不是每隔 8 秒

秒 + 分

/* 每小时的20分8秒时执行 */
@Scheduled(cron="8 20 * * * ?")
public void executeSchedule() {
    //TODO
}

以上表达式含义为:每小时的第 20 分,第 8 秒执行 executeSchedule 方法。分钟后的时间域是小时,由于是 *(星号),所以代表每小时。

秒 + 分 + 小时

/* 每天中午12点20分8秒执行 */
@Scheduled(cron="8 20 12 * * ?")
public void executeSchedule() {
    //TODO
}

以上表达式含义为:每天中午 12 点 20 分 8 秒执行 executeSchedule 方法。小时后的时间域是天,*(星号)表示每天。
注意:时间的范围是 0-23,即 24 时制。所以 12 表示中午 12 点,如果表示午夜 12 点,表达式应该为:cron=8 20 0 * * ?

秒 + 分 + 小时 + 日

/* 每月31号中午12点20分8秒执行 */
@Scheduled(cron="8 20 12 31 * ?")
public void executeSchedule() {
    //TODO
}

以上表达式含义为:每个月的 31 号中午 12 点 20 分 8 秒执行 executeSchedule 方法。
注意:
1、如果某个月份中不包含 31 号,则不执行
2、如果表达式为 cron="8 20 12 31 4 ?",启动项目时将会报错: 4 月份没有 31 号,此表达式永远都不会执行,所以月份与日要配合使用

秒 + 分 + 小时 + 日 + 月

/* 4月30号中午12点20分8秒执行 */
@Scheduled(cron="8 20 12 30 4 ?")
public void executeSchedule() {
    //TODO
}

以上表达式含义为:4 月 30 号中午 12 点 20 分 8 秒执行 executeSchedule 方法。

秒 + 分 + 小时 + 日 + 月 + 星期
星期域具有特殊性,它由 1-7 组成,1 表示星期日(一周的开始),7 表示星期六(一周的最后一天);星期的定义与日和月同时表示,有可能会有冲突。
例子:cron="8 20 12 30 4 3"
以上表达式,看着好像是 星期二 4 月 30 号 中午 12 点 20 分 8 秒这个意思,但实际上并不一定,原因很简单,你怎么就知道 4 月 30 号正好是星期二呢?所以这样定义表达式是存在问题的,也就是说星期在某种程度上是有冲突的。所以一般要在星期之间作出取舍。即定义了星期,就不定义日;定义了日,就不定义星期。不需要定义的时候使用?占位,舍弃谁谁就用?代替
例子:cron="8 20 12 ? 4 2"
以上表达式含义为:4 月份的每个星期 1 的中午 12 点 20 分 8 秒

特殊字符

  • 星号(*):可用在所有时间域中,表示对应时间域的每一个时刻。例如,用于分钟域时,表示每分钟
表达式 含义
cron="8 20 12 * 4 ?" 4月份每天中午12点20分8秒
cron="8 * 18 * 4 ?" 4月份每天下午6点的每分钟的第8秒
  • 问号(?):该字符只在星期域日期域中使用,它通常指定为无意义的值,相当于占位符
  • 减号(-):表达一个范围(range),如,用在小时域16-20表示从下午4点到晚上8点,即 16,17,18,19,20
表达式 含义
cron="8 20 7-9 * 2 ?" 2月份每天7点,8点,9点的20分8秒
  • 逗号(,):表示列表值,如,用在星期域,“1,3,5”,表示星期日,星期二,星期四
表达式 含义
cron="2,59 20 3 * 2 ?" 2月份每天凌晨3点20分的第2秒和第59秒
  • 正斜杠(/):表达一个等步长序列,x为起始值,y为增量步长值。如在分钟域中使用0/25,表示0,25,50秒。也可以使用*/y,同等于0/y
表达式 含义
cron="2,59 0/25 3 * 2 ?" 2月份每天凌晨3点的第2秒和第59秒,3点25(0+25)分的第2秒和第59秒,3点50(25+25)分的第2秒和第59秒
cron="* 15/20 14 * 2 ?" 2月份每天下午2点15分的每一秒,3点35(15+20)分每一秒,3点55(35+20)分的每一秒
  • L:该字符只在日期域星期域中使用,代表Last的意思,但它在两个域中的意义不同。
含义
日期 当前月份的最后一天,如1月31日,非闰年2月28日
星期 表示星期六,同等于7。但如果出现在星期域中,而且在前面还有一个数值X,则表示“这个月的最后X天”,例如:5L表示该月的最后星期四
  • W:该字符只能出现在日期域中,是对前导日期的修饰,表示离该日期最近的工作日。例如12W表示离该月12号最近的工作日,如果该月的12号星期六,则匹配11号星期五;如果12号星期日,则匹配13号星期一;如果12号星期三,那结果就是星期三。但必须注意关联的匹配日期不能够跨月,如指定1W,如果1号星期六,那么结果就是3号星期一,而非上个月的最后的那天。W只能指定单一日期,而不能指定日期范围。
  • LW组合:在日期域可以使用LW,含义是当月的最后一个工作日
  • 井号(#):该字符只能在星期域中使用,表示当月某个工作日
表达式 含义
6#3 当月的第三个星期五(6表示星期五,#3表示当前的第三个)
4#5 当月的第五个星期三。假设当月没有第五个星期三,则忽略不触发
  • C:该字符只在日期域星期域中使用,代表Calendar的意思。意义为计划所关联的日期。如果日期没有被关联,则相当于日历中的所有日期。如。5C日期域中就相当于日历5日以后的第一天1C星期域中相当于星期日后的第一天

cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
cron表达式在线生成器:http://cron.qqe2.com/

小例子

表达式 含义
@Scheduled(cron="0 0 1 1 1 ?") 每年1月1号的凌晨1点执行一次
@Scheduled(cron="0 0 1 1 1,6 ?") 1月和6月的1号的凌晨1点执行一次
@Scheduled(cron="0 0 1 1 1,4,7,10 ?") 每个季度的第1个月的1号的凌晨1点执行一次
@Scheduled(cron="0 0 1 1 * ?") 每月1号凌晨1点执行一次
@Scheduled(cron="0 0 1 *") 每天凌晨1点执行一次

blob
45 声望2 粉丝

A man who concentrates on coding.