@Scheduled 由Spring定义,用于将方法设置为调度任务,可实现方法的周期或定时执行。想单独使用Scheduling,需引入spring-context这个依赖。spring-boot-starter-web已经集成了spring-context,所以可以直接使用Scheduling模块(我使用的 SpringBoo t版本为 2.2.4.RELEASE)。
1.Demo走起
话不多说,先写一个小Demo让定时任务跑起来,然后再一步步添枝加叶层层深入。两个注解【@EnableScheduling + @Scheduled】实现起步:
// 第一个注解@EnableScheduling @EnableScheduling @SpringBootApplication public class SBApplication implements ApplicationRunner { // ApplicationRunner 和 CommandLineRunner 可用于数据配置和预处理 参数类型不一样 public static void main(String[] args) { SpringApplication.run(SBApplication.class, args); } @Bean public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println("Let's inspect the beans provided by Spring Boot:"); String[] beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); /* * for (String name : beanNames) { System.out.println(name); } */ }; } @Override public void run(ApplicationArguments args) throws Exception { /* 可以在springBoot启动时执行预处理程序 */ System.out.println("启动时就打印的数据!"); } } // 第二个注解 @Scheduled @Component public class Scheduler { @Scheduled(fixedRate = 1000, initialDelay = 3000) public void fixedDelay() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String dateString = sdf.format(new Date()); System.out.println(dateString); } }
Demo实现的是项目启动3s后每秒输出当前时间,日志是诚实的…
2021-06-24 15:18:06.798 INFO 16108 --- [)-192.168.0.108] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms 2021-06-24 15:18:09.559 2021-06-24 15:18:10.559 2021-06-24 15:18:11.558 2021-06-24 15:18:12.558 2021-06-24 15:18:13.558 2021-06-24 15:18:14.558 2021-06-24 15:18:15.558
2.@Scheduled用法
// 先看看源码 spring版本 5.2.3.RELEASE @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(Schedules.class) public @interface Scheduled { String CRON_DISABLED = "-"; String cron() default ""; String zone() default ""; long fixedDelay() default -1L; String fixedDelayString() default ""; long fixedRate() default -1L; String fixedRateString() default ""; long initialDelay() default -1L; String initialDelayString() default ""; }
注解说明:
- @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}):表示@Schduled可以被使用在方法和注解上。
- @Retention(RetentionPolicy.RUNTIME):表示@Schduled的生命周期是在程序运行时。
- @Repeatable(Schedules.class):表示@Schduled可以被在同一个地方重复使用多次,参数存放在Schedules类中(我们可以点进去看Schedules类,发现里面有一个Scheduled数组对象,用来保存多个Scheduled的配置)。
属性说明:
- String CRON_DISABLED:定时禁用标志。
- String cron():定义定时执行时间,默认值为空字符串,表示该值无效。
- String zone():定义时区,默认值为空字符串。
- long fixedDelay():定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。默认值-1L,表示该值设置无效。
- String fixedDelayString():定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。与fixedDelay不同只在于值的格式。默认值"",表示该值设置无效。
- long fixedRate():定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。默认值-1L,表示该值无效。
- String fixedRateString():定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。与fixedRate不同只在于值的格式。默认值为空字符串,表示该值无效。
- long initialDelay():定义第一次执行的延迟执行时间,单位秒。默认值-1L,表示没有延迟。
- String initialDelayString():定义第一次执行的延迟执行时间,与initialDelayDelay()不同在于这里使用表达式,而不是以秒为单位。默认值为空字符串,表示没有延迟。
特别注意:
我们要区分fixedDelay和fixedRate的不同,前者是当前方法执行完毕后加上间隔时间再去执行方法,后者不管任务是否执行完毕间隔时间一到就会再次执行方法。
3.cron
前面的demo实现的是简单的延时和频率执行。使用cron可以实现定时定频执行。这里的cron跟Linux上的cron是不同的,Linux上的表达式没有秒,大家要注意。
cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,由于年份非必填所以cron有如下两种语法格式:
- Seconds Minutes Hours DayofMonth Month DayofWeek Year
- Seconds Minutes Hours DayofMonth Month DayofWeek
每一个域可出现的字符如下:
含义:
- *:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。
- ?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
- -:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次。
- /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。
- ,:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
- L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
- W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一 到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
- LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
- #:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
举几个例子【这里是举一反三的环节】 😃
- 每隔5秒执行一次:*/5 * * * * ?
- 每隔1分钟执行一次:0 */1 * * * ? 【如果秒用 * 这样就是每分每秒都执行】
- 每天23点执行一次:0 0 23 * * ? 【如果前面的两个0 用了 * 那就是23点的每分每秒都执行】
- 每月最后一天23点执行一次:0 0 23 L * ?
- 每周星期天凌晨1点实行一次:0 0 1 ? * L
- 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
- 表示在每月的1日的凌晨2点调度任务:0 0 2 1 * ? *
- 表示周一到周五每天上午10:15执行作业:0 15 10 ? * MON-FRI
- 表示2002-2006年的每个月的最后一个星期五上午10:15执行:0 15 10 ? 6L 2002-2006
注意:由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?