Spring Boot中实现定时任务应用实践
前言
在SpringBoot中实现定时任务功能,可以通过Spring自带的定时任务调度,也可以通过集成经典开源组件Quartz实现任务调度。
本文将详细介绍关于SpringBoot实现定时任务应用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
一、Spring定时器
1、cron表达式方式
使用自带的定时任务,非常简单,只需要像下面这样,加上注解就好,不需要像普通定时任务框架那样继承任何定时处理接口,简单示例代码如下:
packagecom.power.demo.scheduledtask.simple;
importcom.power.demo.util.DateTimeUtil;
importorg.springframework.scheduling.annotation.EnableScheduling;
importorg.springframework.scheduling.annotation.Scheduled;
importorg.springframework.stereotype.Component;
importjava.util.Date;
@Component
@EnableScheduling
publicclassSpringTaskA{
/**
*CRON表达式参考:http://cron.qqe2.com/
**/
@Scheduled(cron="*/5****?",zone="GMT+8:00")
privatevoidtimerCron(){
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(timerCron)%s每隔5秒执行一次,记录日志",DateTimeUtil.fmtDate(newDate())));
}
}
SpringTaskA
上述代码中,在一个类上添加@EnableScheduling注解,在方法上加上@Scheduled,配置下cron表达式,一个最最简单的cron定时任务就完成了。cron表达式的各个组成部分,可以参考下面:
@Scheduled(cron="[Seconds][Minutes][Hours][Dayofmonth][Month][Dayofweek][Year]")
2、fixedRate和fixedDelay
@Scheduled注解除了cron表达式,还有其他配置方式,比如fixedRate和fixedDelay,下面这个示例通过配置方式的不同,实现不同形式的定时任务调度,示例代码如下:
packagecom.power.demo.scheduledtask.simple;
importcom.power.demo.util.DateTimeUtil;
importorg.springframework.scheduling.annotation.EnableScheduling;
importorg.springframework.scheduling.annotation.Scheduled;
importorg.springframework.stereotype.Component;
importjava.util.Date;
@Component
@EnableScheduling
publicclassSpringTaskB{
/*fixedRate:上一次开始执行时间点之后5秒再执行*/
@Scheduled(fixedRate=5000)
publicvoidtimerFixedRate(){
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(fixedRate)现在时间:%s",DateTimeUtil.fmtDate(newDate())));
}
/*fixedDelay:上一次执行完毕时间点之后5秒再执行*/
@Scheduled(fixedDelay=5000)
publicvoidtimerFixedDelay(){
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(fixedDelay)现在时间:%s",DateTimeUtil.fmtDate(newDate())));
}
/*第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次*/
@Scheduled(initialDelay=2000,fixedDelay=5000)
publicvoidtimerInitDelay(){
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(initDelay)现在时间:%s",DateTimeUtil.fmtDate(newDate())));
}
}
SpringTaskB
注意一下主要区别:
@Scheduled(fixedRate=5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay=5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=2000,fixedDelay=5000) :第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次
有时候,很多项目我们都需要配置好定时任务后立即执行一次,initialDelay就可以不用配置了。
3、zone
@Scheduled注解还有一个熟悉的属性zone,表示时区,通常,如果不写,定时任务将使用服务器的默认时区;如果你的任务想在特定时区特定时间点跑起来,比如常见的多语言系统可能会定时跑脚本更新数据,就可以设置一个时区,如东八区,就可以设置为:
zone="GMT+8:00"
二、Quartz
Quartz是应用最为广泛的开源任务调度框架之一,有很多公司都根据它实现自己的定时任务管理系统。Quartz提供了最常用的两种定时任务触发器,即SimpleTrigger和CronTrigger,本文以最广泛使用的CronTrigger为例。
1、添加依赖
org.quartz-scheduler quartz 2.3.0
2、配置cron表达式
示例代码需要,在application.properties文件中新增如下配置:
##Quartz定时job配置 job.taska.cron=*/3****? job.taskb.cron=*/7****? job.taskmail.cron=*/5****?
其实,我们完全可以不用配置,直接在代码里面写或者持久化在DB中然后读取也可以。
3、添加定时任务实现
任务1:
packagecom.power.demo.scheduledtask.quartz;
importcom.power.demo.util.DateTimeUtil;
importorg.quartz.DisallowConcurrentExecution;
importorg.quartz.Job;
importorg.quartz.JobExecutionContext;
importorg.quartz.JobExecutionException;
importjava.util.Date;
@DisallowConcurrentExecution
publicclassQuartzTaskAimplementsJob{
@Override
publicvoidexecute(JobExecutionContextvar1)throwsJobExecutionException{
try{
Thread.sleep(1);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(QuartzTaskA)%s每隔3秒执行一次,记录日志",DateTimeUtil.fmtDate(newDate())));
}
}
QuartzTaskA
任务2:
packagecom.power.demo.scheduledtask.quartz;
importcom.power.demo.util.DateTimeUtil;
importorg.quartz.DisallowConcurrentExecution;
importorg.quartz.Job;
importorg.quartz.JobExecutionContext;
importorg.quartz.JobExecutionException;
importjava.util.Date;
@DisallowConcurrentExecution
publicclassQuartzTaskBimplementsJob{
@Override
publicvoidexecute(JobExecutionContextvar1)throwsJobExecutionException{
try{
Thread.sleep(100);
}catch(Exceptione){
e.printStackTrace();
}
System.out.println(String.format("(QuartzTaskB)%s每隔7秒执行一次,记录日志",DateTimeUtil.fmtDate(newDate())));
}
}
QuartzTaskB
定时发送邮件任务:
packagecom.power.demo.scheduledtask.quartz;
importcom.power.demo.service.contract.MailService;
importcom.power.demo.util.DateTimeUtil;
importcom.power.demo.util.PowerLogger;
importorg.joda.time.DateTime;
importorg.quartz.DisallowConcurrentExecution;
importorg.quartz.Job;
importorg.quartz.JobExecutionContext;
importorg.quartz.JobExecutionException;
importorg.springframework.beans.factory.annotation.Autowired;
importjava.util.Date;
@DisallowConcurrentExecution
publicclassMailSendTaskimplementsJob{
@Autowired
privateMailServicemailService;
@Override
publicvoidexecute(JobExecutionContextvar1)throwsJobExecutionException{
System.out.println(String.format("(MailSendTask)%s每隔5秒发送邮件",DateTimeUtil.fmtDate(newDate())));
try{
//Thread.sleep(1);
DateTimedtNow=newDateTime(newDate());
DatestartTime=dtNow.minusMonths(1).toDate();//一个月前
DateendTime=dtNow.plusDays(1).toDate();
mailService.autoSend(startTime,endTime);
PowerLogger.info(String.format("发送邮件,开始时间:%s,结束时间:%s"
,DateTimeUtil.fmtDate(startTime),DateTimeUtil.fmtDate(endTime)));
}catch(Exceptione){
e.printStackTrace();
PowerLogger.info(String.format("发送邮件,出现异常:%s,结束时间:%s",e));
}
}
}
MailSendTask
实现任务看上去非常简单,继承Quartz的Job接口,重写execute方法即可。
4、集成Quartz定时任务
怎么让Spring自动识别初始化Quartz定时任务实例呢?这就需要引用Spring管理的Bean,向Spring容器暴露所必须的bean,通过定义JobFactory实现自动注入。
首先,添加Spring注入的JobFactory类:
packagecom.power.demo.scheduledtask.quartz.config;
importorg.quartz.spi.TriggerFiredBundle;
importorg.springframework.beans.factory.config.AutowireCapableBeanFactory;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.ApplicationContextAware;
importorg.springframework.scheduling.quartz.SpringBeanJobFactory;
publicfinalclassAutowireBeanJobFactoryextendsSpringBeanJobFactory
implementsApplicationContextAware{
privatetransientAutowireCapableBeanFactorybeanFactory;
/**
*Spring提供了一种机制让你可以获取ApplicationContext,即ApplicationContextAware接口
*对于一个实现了ApplicationContextAware接口的类,Spring会实例化它的同时调用它的
*publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException;接口,
*将该bean所属上下文传递给它。
**/
@Override
publicvoidsetApplicationContext(finalApplicationContextcontext){
beanFactory=context.getAutowireCapableBeanFactory();
}
@Override
protectedObjectcreateJobInstance(finalTriggerFiredBundlebundle)
throwsException{
finalObjectjob=super.createJobInstance(bundle);
beanFactory.autowireBean(job);
returnjob;
}
}
AutowireBeanJobFactory
定义QuartzConfig:
packagecom.power.demo.scheduledtask.quartz.config;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Qualifier;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.scheduling.quartz.CronTriggerFactoryBean;
importorg.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
publicclassQuartzConfig{
@Autowired
@Qualifier("quartzTaskATrigger")
privateCronTriggerFactoryBeanquartzTaskATrigger;
@Autowired
@Qualifier("quartzTaskBTrigger")
privateCronTriggerFactoryBeanquartzTaskBTrigger;
@Autowired
@Qualifier("mailSendTrigger")
privateCronTriggerFactoryBeanmailSendTrigger;
//Quartz中的job自动注入spring容器托管的对象
@Bean
publicAutowireBeanJobFactoryautoWiringSpringBeanJobFactory(){
returnnewAutowireBeanJobFactory();
}
@Bean
publicSchedulerFactoryBeanschedulerFactoryBean(){
SchedulerFactoryBeanscheduler=newSchedulerFactoryBean();
scheduler.setJobFactory(autoWiringSpringBeanJobFactory());//配置Spring注入的Job类
//设置CronTriggerFactoryBean,设定任务Trigger
scheduler.setTriggers(
quartzTaskATrigger.getObject(),
quartzTaskBTrigger.getObject(),
mailSendTrigger.getObject()
);
returnscheduler;
}
}
QuartzConfig
接着配置job明细:
packagecom.power.demo.scheduledtask.quartz.config;
importcom.power.demo.common.AppField;
importcom.power.demo.scheduledtask.quartz.MailSendTask;
importcom.power.demo.scheduledtask.quartz.QuartzTaskA;
importcom.power.demo.scheduledtask.quartz.QuartzTaskB;
importcom.power.demo.util.ConfigUtil;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.scheduling.quartz.CronTriggerFactoryBean;
importorg.springframework.scheduling.quartz.JobDetailFactoryBean;
@Configuration
publicclassTaskSetting{
@Bean(name="quartzTaskA")
publicJobDetailFactoryBeanjobDetailAFactoryBean(){
//生成JobDetail
JobDetailFactoryBeanfactory=newJobDetailFactoryBean();
factory.setJobClass(QuartzTaskA.class);//设置对应的Job
factory.setGroup("quartzTaskGroup");
factory.setName("quartzTaskAJob");
factory.setDurability(false);
factory.setDescription("测试任务A");
returnfactory;
}
@Bean(name="quartzTaskATrigger")
publicCronTriggerFactoryBeancronTriggerAFactoryBean(){
Stringcron=ConfigUtil.getConfigVal(AppField.JOB_TASKA_CRON);
CronTriggerFactoryBeanstFactory=newCronTriggerFactoryBean();
//设置JobDetail
stFactory.setJobDetail(jobDetailAFactoryBean().getObject());
stFactory.setStartDelay(1000);
stFactory.setName("quartzTaskATrigger");
stFactory.setGroup("quartzTaskGroup");
stFactory.setCronExpression(cron);
returnstFactory;
}
@Bean(name="quartzTaskB")
publicJobDetailFactoryBeanjobDetailBFactoryBean(){
//生成JobDetail
JobDetailFactoryBeanfactory=newJobDetailFactoryBean();
factory.setJobClass(QuartzTaskB.class);//设置对应的Job
factory.setGroup("quartzTaskGroup");
factory.setName("quartzTaskBJob");
factory.setDurability(false);
factory.setDescription("测试任务B");
returnfactory;
}
@Bean(name="quartzTaskBTrigger")
publicCronTriggerFactoryBeancronTriggerBFactoryBean(){
Stringcron=ConfigUtil.getConfigVal(AppField.JOB_TASKB_CRON);
CronTriggerFactoryBeanstFactory=newCronTriggerFactoryBean();
//设置JobDetail
stFactory.setJobDetail(jobDetailBFactoryBean().getObject());
stFactory.setStartDelay(1000);
stFactory.setName("quartzTaskBTrigger");
stFactory.setGroup("quartzTaskGroup");
stFactory.setCronExpression(cron);
returnstFactory;
}
@Bean(name="mailSendTask")
publicJobDetailFactoryBeanjobDetailMailFactoryBean(){
//生成JobDetail
JobDetailFactoryBeanfactory=newJobDetailFactoryBean();
factory.setJobClass(MailSendTask.class);//设置对应的Job
factory.setGroup("quartzTaskGroup");
factory.setName("mailSendTaskJob");
factory.setDurability(false);
factory.setDescription("邮件发送任务");
returnfactory;
}
@Bean(name="mailSendTrigger")
publicCronTriggerFactoryBeancronTriggerMailFactoryBean(){
Stringcron=ConfigUtil.getConfigVal(AppField.JOB_TASKMAIL_CRON);
CronTriggerFactoryBeanstFactory=newCronTriggerFactoryBean();
//设置JobDetail
stFactory.setJobDetail(jobDetailMailFactoryBean().getObject());
stFactory.setStartDelay(1000);
stFactory.setName("mailSendTrigger");
stFactory.setGroup("quartzTaskGroup");
stFactory.setCronExpression(cron);
returnstFactory;
}
}
TaskSetting
最后启动你的SpringBoot定时任务应用,一个完整的基于Quartz调度的定时任务就实现好了。
本文定时任务示例中,有一个定时发送邮件任务MailSendTask,下一篇将分享SpringBoot应用中以MongoDB作为存储介质的简易邮件系统。
扩展阅读:
很多公司都会有自己的定时任务调度框架和系统,在SpringBoot中如何整合Quartz集群,实现动态定时任务配置?
参考:
https://www.nhooo.com/article/139591.htm
https://www.nhooo.com/article/139597.htm
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。