SpringBoot实现定时任务
写在前面
在实际工作中,定时任务是一个很常见的功能,如定时统计订单数、数据库备份、定时发送短信和邮件、定时统计博客访客等等,简单的定时任务可以直接通过Spring提供的@Scheduled
注解来实现,复杂一点的定时任务则可以通过集成Quartz
([kwɔːts])来实现,本篇将分别介绍如何使用这两种方式实现定时任务。
项目初始化
新建一个名为time-task
的SpringBoot项目,后续将在该项目中进行定时任务的实现。
@Scheduled
注解方式
小试牛刀
第一步,添加依赖。在项目的POM文件中新增Web依赖:
1 | <dependency> |
第二步,开启定时任务。在项目启动类上添加@EnableScheduling
注解,相应的代码为:
1 | @EnableScheduling |
第三步,配置定时任务。定时任务主要是通过@Scheduled
注解来进行配置。新建一个component包,并在其中创建一个FirstSchedule
类,其中的代码为:
1 | @Component |
简单解释一下上述代码的含义:
- 通过
@Scheduled
注解来标注一个定时任务,其中fixedDelay=1000
表示在当前任务执行结束1秒后开启另一个任务;fixedRate=2000
表示在当前任务执行2秒后开启另一个定时任务;initialDelay=1000
则表示首次执行的延迟时间为1秒,即任务首次启动任务的延迟时间。 - 在
@Scheduled
注解中也可以使用cron表达式,cron="0 * * * * ?"
表示该定时任务每分钟执行一次。 - 以上注解值的单位默认为
TimeUnit.MILLISECONDS
,即毫秒。
配置完成后,接下来启动SpringBoot项目,定时任务部分打印日志如下所示:
cron表达式
cron表达式由6个部分组成,从左到右分别是:秒(second)、分(minute)、时(hour)、日(day of month)、月(month)、周(day of week):
1 | ┌───────────── second (0-59) |
各个部分的取值情况如下表所示:
部分 | 是否为必填项 | 允许填写的值 | 允许的通配符 |
---|---|---|---|
秒(second) | 是 | 0-59 | - * / |
分(minute) | 是 | 0-59 | - * / |
时(hour) | 是 | 0-23 | - * / |
日(day of month) | 是 | 1-31 | - * ? / L W |
月(month) | 是 | 1-12 or JAN-DEC | - * / |
周(day of week) | 是 | 0-7 or SUN-SAT | - * ? / L # |
解释一下上述通配符的含义:
(1)?
表示不指定值,如果开发者不关心某个部分的取值时,就可以使用它。请注意,月份中的日期和星期可能会起冲突,因此在配置时这两个必须有一个值是?
;
(2)*
表示所有值,举个例子,当你在“秒”上设置*
时,则表示每秒都会触发;
(3),
用于分开多个值,举个例子,当你在“周”上设置 “MON,WED,FRI”,分别表示周一,周三和周五触发;
(4)-
表示区间,举个例子,当你在“秒”上设置 “10-12”,则表示10,11,12秒都会触发;
(5)/
用于递增触发,举个例子,当你在“秒”上面设置”5/15”,则表示从5秒开始,每增15秒触发(5,20,35,50);
(6)#
表示序号(即每月的第几个周几),举个例子,当你在“周”上设置”6#3”,则表示在每月的第三个周六,这种可以用在母亲节和父亲节之类的动态变化节日上面。请注意,在“周”这一部分中,英文字母是不分大小写的,即MON与mon是一样的;
(7)L
表示最后。用在“日”上,表示当月的最后一天(依据当前月份,如果是二月系统会自动判断是否是润年)。 在“周”字段上表示星期六,相当于”7”或”SAT”(周日是第一天,即每个星期的开始)。如果在”L”前加上数字,则表示该数据的最后一个。举个例子,当你在“周”上设置”6L”,则表示”本月最后一个星期五”;
(8)W
表示离指定日期最近的工作日(周一至周五)。举个例子,如果你在“日”上设置”15W”,则表示离每月15号最近的那个工作日触发。如果15号是周六,那么找最近的周五(14号)触发,如果15号是周未,则找最近的下周一(16号)触发,如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,”W”前只能设置具体的数字,不允许区间”-“)
(9)L
和W
可以组合使用。举个例子,如果在“日”上设置”LW”,则表示在本月的最后一个工作日触发,一般用于发薪日。
一些常用的例子
(1)0 0 * * * *
,表示每天每个小时的开始,即0分0秒;
(2)*/10 * * * * *
,表示每10秒钟;
(3)0 0 8-10 * * *
,表示每天8点、9点和10点;
(4)0 0 6,19 * * *
,表示每天早上 6:00 和晚上 7:00;
(5)0 0/30 8-10 * * *
,表示每天 8:00、8:30、9:00、9:30、10:00 和 10:30;
(6)0 0 9-17 * * MON-FRI
,表示工作日朝九晚五;
(7)0 0 0 25 12 ?
,表示每个圣诞节的午夜;
(8)0 0 0 L * *
,表示每月最后一天的午夜;
(9)0 0 0 L-3 * *
,表示每月倒数第三天午夜;
(10)0 0 0 1W * *
,表示每月第一个工作日的午夜;
(11)0 0 0 LW * *
,表示每月最后一个工作日的午夜;
(12)0 0 0 * * 5L
,表示每月最后一个星期五午夜;
(13)0 0 0 * * THUL
,表示每月最后一个星期四的午夜;
(14)0 0 0 ? * 5#2
,表示每月第二个星期五午夜;
(15)0 0 0 ? * MON#1
,表示每月第一个星期一的午夜。
Quartz 方式
Quartz 简介
Quartz是一个功能丰富的开源作业调度库,它由Java编写,可以集成在任何Java应用程序中。开发者可以使用Quartz来创建简单或者复杂的执行计划,它支持数据库、集群、插件以及邮件,且支持cron表达式,具有极高的灵活性。
一般来说,除非定时任务业务非常简单,否则一般是不会使用@Scheduled
注解方式,而是使用Quartz框架。
小试牛刀
第一步,添加依赖。在项目的POM文件中新增Quartz依赖:
1 | <dependency> |
第二步,开启定时任务。在项目启动类上添加@EnableScheduling
注解,相应的代码为:
1 | @EnableScheduling |
第三步,配置定时任务。Quartz有三个比较重要的概念,其中JobDetail是用户要做的事情;
Trigger是触发器,即事情什么时候做;SchedulerFactory是调度工厂,里面包含多个触发器。
我们先要确定用户要做的事情(JobDetail),由于用户要做的可能不是一件事,因此需要先定义每件事情(Job)。定义Job有两种方式,可以直接定义Bean或者继承QuartzJobBean这一方式。
新建一个component包,如果我们选择“直接定义Bean”这一方式,那么在其中创建一个FirstJob
类,其中的代码为:
1 | @Component |
这种方式比较简单,直接将这个Bean注册到Spring容器中,但是也有缺点,无法传递参数。如果开发者需要传递参数,那么可以选择“继承QuartzJobBean”这一方式,在其中创建一个SecondJob
类,其中的代码为:
1 | public class SecondJob extends QuartzJobBean { |
接下来新建一个config包,并在其中创建QuartzConfig
类用于对JobDetail
和Trigger
进行配置,相应的代码为:
1 | @Configuration |
解释一下上述代码的含义:
JobDetail
的配置方式方式:第一种方式通过MethodInvokingJobDetailFactoryBean
类配置JobDetail
,只需要指定Job的实例名称和要调用的方法即可,注册这种方式无法在创建JobDetail
时传递参数;第二种方式是通过JobDetailFactoryBean
来实现的,这种方式只需要指定JobClass
即可,当然可以通过JobDataMap
传递参数到Job中,Job中只需要提供属性名,并且提供一个相应的set方法即可接收到参数。Trigger
有多种不同实现,这里展示两种最常使用的Trigger
:SimpleTrigger
和CronTrigger
,这两种Trigger
分别使用SimpleTriggerFactoryBean
和CronTriggerFactoryBean
进行创建。在SimpleTriggerFactoryBean
对象中,首先设置JobDetail
,然后通过setRepeatCount
配置任务循环次数,setStartDelay
配置任务启动延迟时间,setRepeatInterval
配置任务的时间间隔。在CronTriggerFactoryBean
对象中,则主要配置JobDetail
和Cron表达式。- 最后通过
SchedulerFactoryBean
创建SchedulerFactory
对象,然后配置Trigger
即可。
经过这几步的配置,定时任务就配置成功了。接下来启动SpringBoot项目,可以看到控制台输出一些信息: