记录一下SpringBoot+@EnableScheduling使用定时器的常见案例
我的SpringBoot版本
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent>
启动类
package boot.example.timer;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling
public class App {public static void main( String[] args ){SpringApplication.run(App.class, args);System.out.println( "Hello World!" );}
}
必须加上这个注解 @EnableScheduling
定时器TimerService
package boot.example.timer.timer;import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;@Service
public class TimerService {@Scheduled(cron = "0/1 * * * * ?")public void timerTest() throws IOException {long now = System.currentTimeMillis();DateFormat format = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");System.out.println("timer--"+format.format(now));}
}
有很多的cron表达式来指定任务在需要的时间执行,可是有时候让人头疼,明明设置的cron是对的,可就是到点不执行,回过头来发现cron就是写错了,这很尴尬。
cron表达式 秒钟(1) 分钟(2) 小时(3) 日期(4) 月份(5) 星期(6) 年(7) 长度是7 可为啥我们在程序里写的长度只有6呢?
比如我每秒钟执行一次
@Scheduled(cron = "0/1 * * * * * ?") // SpringBoot运行报错
@Scheduled(cron = "0/1 * * * * ?") // SpringBoot运行正常
那是因为年(7) 稍微特殊,他可以是空或者1970-2099 允许的字符有 , - * / 四种符号,实际没有(问号) ,可能是按照规律去推以为有问号而已。
那么这个?问号到底是啥意思?
?问号字符只在日期(4)和星期(6)中使用,表示不确定的值 当需要的时候在使用,日期(4)和星期(6)是互相拉扯的,用?问号可以表示不想设置。
秒(1)的案例
1分钟有60秒 因此允许值范围是0-59
符号*表示每秒
符号,表示指定的秒数触发 比如 0,2,4,6 那么就在单位分钟内的 0 2 4 6那个点触发执行
符号-表示指定范围区间 比如 20-40 那么就是在20到40s的区间内每秒执行
符号/表示步进 比如 0/1 从0秒开始每秒触发 0/10 从0秒开始每隔10s触发一次
1.1s执行一次
@Scheduled(cron = "* * * * * ?")
@Scheduled(cron = "0/1 * * * * ?")
2.指定在 2s 17s 34s 52s 这四个秒钟点执行
@Scheduled(cron = "2,17,34,52 * * * * ?")
3.指定在20 - 30s内每秒执行
@Scheduled(cron = "20-30 * * * * ?")
测试可以看到是闭区间的
4.从0秒开始每隔10s触发一次
@Scheduled(cron = "0/10 * * * * ?")
5.从5秒开始每隔40s触发一次 注意这里!!!!
@Scheduled(cron = "5/40 * * * * ?")
从运行的打印日志可以看到确实是从5s开始 40s后到了45秒执行了一次 可下一个40s就不是这么算的了 也就是说并不是每次间隔40s了,这里是20s 40s 如此的 相当于在5s执行了一次 45s执行了一次,原因嘛,反正就那样执行的。
6.在5-40s闭区间内每隔2s执行一次,这种是两种符号的一起用
@Scheduled(cron = "5-40/2 * * * * ?")
其他符号的组合?不尝试了,有空再说。
分钟(2)的案例
1小时有60分钟 因此允许值范围是0-59 和 秒一样的范围
符号*表示每分钟
符号,表示指定的分钟触发 比如 0,2,4,6 那么就在单位小时内的指定分钟数 0 2 4 6分那个点触发执行
符号-表示指定范围区间 比如 20-40 那么就是在20到40分钟的点位区间内每分钟执行
符号/表示步进 比如 0/1 从0分钟开始每分钟触发, 0/10 从0分钟开始每隔10分钟触发一次
可以发现这些符号的意思很通用
1.1分钟执行一次,此时秒的那一项设置为0
@Scheduled(cron = "0 * * * * ?")
@Scheduled(cron = "0 0/1 * * * ?")
2.指定在 26 28 32 34 这四个分钟点执行
@Scheduled(cron = "0 26,28,32,34 * * * ?")
3.指定在38 - 45闭区间这段分钟时间内每分钟执行
@Scheduled(cron = "0 38-45 * * * ?")
4.从0分钟开始每隔2分钟触发一次
@Scheduled(cron = "0 0/2 * * * ?")
5.从5分钟开始每隔40分钟触发一次(实际并不完全是40分钟)
@Scheduled(cron = "0 5/40 * * * ?")
这里和分钟同样的问题 执行实际上是 每小时5分那个点和45分那个点执行 那就是说1个小时内执行两次 5分到45分 间隔40分钟,45分到下一个5分间隔20分钟,这里得注意并不是每隔40分钟。
6.在20-40分钟闭区间内每隔3分钟执行一次,这种是两种符号的一起用
@Scheduled(cron = "0 20-40/3 * * * ?")
其他的分钟相关的不尝试了。
小时(3)的案例
一天有24小时 因此允许值范围是0-23
符号*表示每小时
符号,表示指定的小时钟点触发 比如 1,6, 12, 19那么就在一天的 1 6 12 19点钟触发执行
符号-表示指定范围区间 比如 8-20 那么就是在早8点到晚上8点每隔1个小时执行一次
符号/表示步进 比如 0/1 从0时开始每小时触发 0/2 从0点钟开始每隔2小时触发一次,2/3 从2点钟开始每隔3小时执行一次
1.每小时执行一次
@Scheduled(cron = "0 0 * * * ?")
@Scheduled(cron = "0 0 0/1 * * ?")
2.在15点到17点之间每隔一小时执行一次
@Scheduled(cron = "0 0 15-17 * * ?")
3.指定在 2点钟 10点钟 12点钟 18点钟 这四个小时点位执行
@Scheduled(cron = "0 0 2,10,12,18 * * ?")
4.从0时开始每隔4小时触发一次
@Scheduled(cron = "0 0 0/4 * * ?")
5.从2点钟开始每隔18小时触发一次 注意!!!!
@Scheduled(cron = "0 0 2/18 * * ?")
这里没去测试了,等的时间太久了,2点钟执行一次 18小时候20点钟执行一下,下一个就超过了24小时,因此在第二天的2点钟执行一次,这样间隔是6小时 18小时,并不一定是每次间隔18小时的,得注意小时的范围是0-23。
6.在2-18点钟闭区间内每隔3小时执行一次,这种是两种符号的一起用
@Scheduled(cron = "0 0 2-18/3 * * ?")
日期(4)的案例
一个月大概是30天 有31天的 因此允许值范围是1-31 如果说当月没有第31天 那么会抛出异常
符号*表示每天
符号,表示指定的天触发 比如 1,3, 5, 7那么就在当月的 第一天,第三天,第五天,第七天触发执行
符号-表示指定范围区间 比如 2-20 那么就是在当月的第二天到第二十天之间每隔一天执行一次,闭区间内,第二天和第二十天也包含在内
符号/表示步进 比如 2/3表示从第2天开始每隔3天执行一次,1/1表示从第一天开始每天执行一次
符号?表示不确定的值,很少用到的
符号L在这里表示当月的最后一天
符号W表示指定离给定日期最近的工作日(周一到周五) 没用过
符号C意思是Calendar,没用过
这里写几个案例(未测试验证)
1.每天执行一次
@Scheduled(cron = "0 0 0 1/1 * ?")
@Scheduled(cron = "0 0 0 * * ?")
2.当月的1,3,5,7四天执行一次
@Scheduled(cron = "0 0 0 1,3,5,7 * ?")
3.2-20日每天执行一次
@Scheduled(cron = "0 0 0 2-20 * ?")
4.10-25日每隔三天执行
@Scheduled(cron = "0 0 0 10-25/3 * ?")
月份(5)的案例
一年有12个月 因此允许值范围是1-12,或者英文JAN-DEC
符号*表示每个月
符号,表示指定的月份触发 比如 1,6, 12那么就在1月 6月 12月触发执行
符号-表示指定范围区间 比如 2-8 那么就是在2月到8月的闭区间内每月执行一次
符号/表示步进 比如 1/1 从1月开始每隔一个月触发一次,2/3 从2月开始每隔3个月执行一次
英文12个月份的缩写
1月-1-JAN
2月-2-FEB
3月-3-MAR
4月-4-APR
5月-5-MAY
6月-6-JUN
7月-7-JUL
8月-8-AUG
9月-9-SEP
10月-10-OCT
11月-11-NOV
12月-12-DEC
这里写几个案例(未测试验证)
1.每月执行一次
@Scheduled(cron = "0 0 0 1 * ?")
@Scheduled(cron = "0 0 0 1 1/1 ?")
2.2月,4月,6月,8月执行一次
@Scheduled(cron = "0 0 0 1 2,4,6,8 ?")
@Scheduled(cron = "0 0 0 1 FEB,APR,JUN,AUG ?")
3.2月-10月每隔三个月执行一次
@Scheduled(cron = "0 0 0 1 2-10/3 ?")
@Scheduled(cron = "0 0 0 1 FEB-OCT/3 ?")
其他的遇到再记录
星期(6)的案例
一个星期有七天 因此允许值范围是1-7 当然也可以英文字母(SUN-SAT)
符号*表示每个星期
符号,表示指定的星期几触发
符号-表示指定范围区间
符号/表示步进
符号L 当用在星期里的话表示星期的最后一天也就是星期六触发,在L前面加数字 比如2L 3L这种表示每个月的最后一个星期的星期几执行一次,有些难以理解,很多博客资料都这样说就这样定吧,测试的话花的时间太长,免了不去验证了!
其他符号略过
这里着重注意!!!!!
星期的话 用英文字母 :星期天-SUN, 星期一-MON, 星期二-TUE, 星期三-WED, 星期四-THU,星期五-FRI,星期六-STA
星期的话 用数字 是从星期一开始的,并不是星期天,这倒是有个坑在这里儿,也就是说 星期天-7,星期一-1,星期二-2,星期三-3,星期四-4,星期五-5,星期六-6
我这里使用的是SpringBoot2.0.x做的测试
可以证明至少在这个版本1-7指的是星期一到星期天
可以查日历知道2023-07-19就是星期三 我写了一个执行的cron 星期三对应的就是3
就是说1对应的不是星期天 而是星期一
再看直接写WED 星期三
所以建议使用字母方式
这里写几个案例(未测试验证)
1.每周星期三的零点零分零秒执行一次
@Scheduled(cron = "0 0 0 ? * WED")
@Scheduled(cron = "0 0 0 ? * 3")
2.星期天,星期二,星期四零点零分零秒执行一次
@Scheduled(cron = "0 0 0 ? * SUN,TUE,THU")
@Scheduled(cron = "0 0 0 ? * 7,2,4")
3.工作日每天零点零分零秒执行一次(星期一到星期五)
@Scheduled(cron = "0 0 0 ? * MON-FRI")
@Scheduled(cron = "0 0 0 ? * 1-5")
4.星期一到星期天 每隔两天执行一次
@Scheduled(cron = "0 0 0 ? * MON-STA/2")
@Scheduled(cron = "0 0 0 ? * 1-6/2")
其他单独关于星期的案例用到再补充
年(7)的案例
这个世界有多少年,都不知道,目前年份很少用到,要么留空,要么区间1970-2099年闭区间内
符号*表示每年
符号,表示指定的年触发 比如 2023,2025, 2030那么就在那年会触发执行
符号-表示指定范围区间 比如 2023-2033 那么就是在2023年到2033年每年执行一次
符号/表示步进 比如 2023/1 从2023年开始每年触发一次 2023/3 从2023年开始每隔3年执行一次
按年的例子,没有用到过,随意尝试了几个,程序都报错,也不是刚需,于是不去折腾例子了,先按照符号表达的意思去理解,如果有问题在更改。