在Spring Boot中实现定时任务功能,可以通过Spring自带的定时任务调度,也可以通过集成经典开源组件Quartz实现任务调度。
使用自带的定时任务,非常简单,只需要像下面这样,加上注解就好,不需要像普通定时任务框架那样继承任何定时处理接口 ,简单示例代码如下:
package com.power.demo.scheduledtask.simple; import com.power.demo.util.DateTimeUtil; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Date; @Component @EnableScheduling public class SpringTaskA { /** * CRON表达式参考: http://cron.qqe2.com/ * */ @Scheduled(cron = "*/5 * * * * ?", zone = "GMT+8:00" ) private void timerCron() { try { Thread.sleep( 100 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(timerCron)%s 每隔5秒执行一次,记录日志", DateTimeUtil.fmtDate( new Date()))); } }
上述代码中,在一个类上添加@EnableScheduling注解,在方法上加上@Scheduled,配置下 cron 表达式,一个最最简单的cron定时任务就完成了。cron表达式的各个组成部分,可以参考下面:
@Scheduled(cron = "[Seconds] [Minutes] [Hours] [Day of month] [Month] [Day of week] [Year]")
@Scheduled注解除了cron表达式,还有其他配置方式,比如fixedRate和fixedDelay,下面这个示例通过配置方式的不同,实现不同形式的定时任务调度,示例代码如下:
package com.power.demo.scheduledtask.simple; import com.power.demo.util.DateTimeUtil; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Date; @Component @EnableScheduling public class SpringTaskB { /* fixedRate:上一次开始执行时间点之后5秒再执行 */ @Scheduled(fixedRate = 5000 ) public void timerFixedRate() { try { Thread.sleep( 100 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(fixedRate)现在时间:%s", DateTimeUtil.fmtDate( new Date()))); } /* fixedDelay:上一次执行完毕时间点之后5秒再执行 */ @Scheduled(fixedDelay = 5000 ) public void timerFixedDelay() { try { Thread.sleep( 100 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(fixedDelay)现在时间:%s", DateTimeUtil.fmtDate( new Date()))); } /* 第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次 */ @Scheduled(initialDelay = 2000, fixedDelay = 5000 ) public void timerInitDelay() { try { Thread.sleep( 100 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(initDelay)现在时间:%s", DateTimeUtil.fmtDate( new Date()))); } }
注意一下主要区别:
@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=2000, fixedDelay=5000) :第一次延迟2秒后执行,之后按fixedDelay的规则每5秒执行一次
有时候,很多项目我们都需要配置好定时任务后立即执行一次,initialDelay就可以不用配置了。
@Scheduled注解还有一个熟悉的属性zone,表示时区,通常,如果不写,定时任务将使用服务器的默认时区;如果你的任务想在特定时区特定时间点跑起来,比如常见的多语言系统可能会定时跑脚本更新数据,就可以设置一个时区,如东八区,就可以设置为:
zone = "GMT+8:00"
Quartz是应用最为广泛的开源任务调度框架之一,有很多公司都根据它实现自己的定时任务管理系统。Quartz提供了最常用的两种定时任务触发器,即SimpleTrigger和CronTrigger,本文以最广泛使用的CronTrigger为例。
< dependency > < groupId > org.quartz-scheduler </ groupId > < artifactId > quartz </ artifactId > < version > 2.3.0 </ version > </ dependency >
示例代码需要,在application.properties文件中新增如下配置:
## Quartz定时job配置 job.taska.cron =*/3 * * * * ? job.taskb.cron =*/7 * * * * ? job.taskmail.cron =*/5 * * * * ?
其实,我们完全可以不用配置,直接在代码里面写或者持久化在DB中然后读取也可以。
任务1:
package com.power.demo.scheduledtask.quartz; import com.power.demo.util.DateTimeUtil; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; @DisallowConcurrentExecution public class QuartzTaskA implements Job { @Override public void execute(JobExecutionContext var1) throws JobExecutionException { try { Thread.sleep( 1 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(QuartzTaskA)%s 每隔3秒执行一次,记录日志", DateTimeUtil.fmtDate( new Date()))); } }
任务2:
package com.power.demo.scheduledtask.quartz; import com.power.demo.util.DateTimeUtil; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; @DisallowConcurrentExecution public class QuartzTaskB implements Job { @Override public void execute(JobExecutionContext var1) throws JobExecutionException { try { Thread.sleep( 100 ); } catch (Exception e) { e.printStackTrace(); } System.out.println(String.format( "(QuartzTaskB)%s 每隔7秒执行一次,记录日志", DateTimeUtil.fmtDate( new Date()))); } }
定时发送邮件任务:
package com.power.demo.scheduledtask.quartz; import com.power.demo.service.contract.MailService; import com.power.demo.util.DateTimeUtil; import com.power.demo.util.PowerLogger; import org.joda.time.DateTime; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; @DisallowConcurrentExecution public class MailSendTask implements Job { @Autowired private MailService mailService; @Override public void execute(JobExecutionContext var1) throws JobExecutionException { System.out.println(String.format( "(MailSendTask)%s 每隔5秒发送邮件", DateTimeUtil.fmtDate( new Date()))); try { // Thread.sleep(1); DateTime dtNow = new DateTime( new Date()); Date startTime = dtNow.minusMonths(1).toDate(); // 一个月前 Date endTime = dtNow.plusDays(1 ).toDate(); mailService.autoSend(startTime, endTime); PowerLogger.info(String.format( "发送邮件,开始时间:%s,结束时间:%s" , DateTimeUtil.fmtDate(startTime), DateTimeUtil.fmtDate(endTime))); } catch (Exception e) { e.printStackTrace(); PowerLogger.info(String.format( "发送邮件,出现异常:%s,结束时间:%s" , e)); } } }
实现任务看上去非常简单,继承Quartz的Job接口,重写execute方法即可。
怎么让Spring自动识别初始化Quartz定时任务实例呢?这就需要引用Spring管理的Bean,向Spring容器暴露所必须的bean,通过定义Job Factory实现自动注入。
首先,添加Spring注入的Job Factory类:
package com.power.demo.scheduledtask.quartz.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SpringBeanJobFactory; public final class AutowireBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; /** * Spring提供了一种机制让你可以获取ApplicationContext,即ApplicationContextAware接口 * 对于一个实现了ApplicationContextAware接口的类,Spring会实例化它的同时调用它的 * public voidsetApplicationContext(ApplicationContext applicationContext) throws BeansException;接口, * 将该bean所属上下文传递给它。 * */ @Override public void setApplicationContext( final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance( final TriggerFiredBundle bundle) throws Exception { final Object job = super .createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
定义QuartzConfig:
package com.power.demo.scheduledtask.quartz.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; @Configuration public class QuartzConfig { @Autowired @Qualifier( "quartzTaskATrigger" ) private CronTriggerFactoryBean quartzTaskATrigger; @Autowired @Qualifier( "quartzTaskBTrigger" ) private CronTriggerFactoryBean quartzTaskBTrigger; @Autowired @Qualifier( "mailSendTrigger" ) private CronTriggerFactoryBean mailSendTrigger; // Quartz中的job自动注入spring容器托管的对象 @Bean public AutowireBeanJobFactory autoWiringSpringBeanJobFactory() { return new AutowireBeanJobFactory(); } @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean scheduler = new SchedulerFactoryBean(); scheduler.setJobFactory(autoWiringSpringBeanJobFactory()); // 配置Spring注入的Job类 // 设置CronTriggerFactoryBean,设定任务Trigger scheduler.setTriggers( quartzTaskATrigger.getObject(), quartzTaskBTrigger.getObject(), mailSendTrigger.getObject() ); return scheduler; } }
接着配置job明细:
package com.power.demo.scheduledtask.quartz.config; import com.power.demo.common.AppField; import com.power.demo.scheduledtask.quartz.MailSendTask; import com.power.demo.scheduledtask.quartz.QuartzTaskA; import com.power.demo.scheduledtask.quartz.QuartzTaskB; import com.power.demo.util.ConfigUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; @Configuration public class TaskSetting { @Bean(name = "quartzTaskA" ) public JobDetailFactoryBean jobDetailAFactoryBean() { // 生成JobDetail JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(QuartzTaskA. class ); // 设置对应的Job factory.setGroup("quartzTaskGroup" ); factory.setName( "quartzTaskAJob" ); factory.setDurability( false ); factory.setDescription( "测试任务A" ); return factory; } @Bean(name = "quartzTaskATrigger" ) public CronTriggerFactoryBean cronTriggerAFactoryBean() { String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKA_CRON); CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean(); // 设置JobDetail stFactory.setJobDetail(jobDetailAFactoryBean().getObject()); stFactory.setStartDelay( 1000 ); stFactory.setName( "quartzTaskATrigger" ); stFactory.setGroup( "quartzTaskGroup" ); stFactory.setCronExpression(cron); return stFactory; } @Bean(name = "quartzTaskB" ) public JobDetailFactoryBean jobDetailBFactoryBean() { // 生成JobDetail JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(QuartzTaskB. class ); // 设置对应的Job factory.setGroup("quartzTaskGroup" ); factory.setName( "quartzTaskBJob" ); factory.setDurability( false ); factory.setDescription( "测试任务B" ); return factory; } @Bean(name = "quartzTaskBTrigger" ) public CronTriggerFactoryBean cronTriggerBFactoryBean() { String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKB_CRON); CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean(); // 设置JobDetail stFactory.setJobDetail(jobDetailBFactoryBean().getObject()); stFactory.setStartDelay( 1000 ); stFactory.setName( "quartzTaskBTrigger" ); stFactory.setGroup( "quartzTaskGroup" ); stFactory.setCronExpression(cron); return stFactory; } @Bean(name = "mailSendTask" ) public JobDetailFactoryBean jobDetailMailFactoryBean() { // 生成JobDetail JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(MailSendTask. class ); // 设置对应的Job factory.setGroup("quartzTaskGroup" ); factory.setName( "mailSendTaskJob" ); factory.setDurability( false ); factory.setDescription( "邮件发送任务" ); return factory; } @Bean(name = "mailSendTrigger" ) public CronTriggerFactoryBean cronTriggerMailFactoryBean() { String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKMAIL_CRON); CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean(); // 设置JobDetail stFactory.setJobDetail(jobDetailMailFactoryBean().getObject()); stFactory.setStartDelay( 1000 ); stFactory.setName( "mailSendTrigger" ); stFactory.setGroup( "quartzTaskGroup" ); stFactory.setCronExpression(cron); return stFactory; } }
最后启动你的Spring Boot定时任务应用,一个完整的基于Quartz调度的定时任务就实现好了。
本文定时任务示例中,有一个定时发送邮件任务MailSendTask,下一篇将分享Spring Boot应用中以MongoDB作为存储介质的简易邮件系统。
扩展阅读:
很多公司都会有自己的定时任务调度框架和系统,在Spring Boot中如何整合Quartz集群,实现动态定时任务配置?
参考:
http://www.cnblogs.com/vincent0928/p/6294792.html
http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html
凌云 2018-12-14
白诗秀儿 2016-03-24
凌云 2018-02-12
荡神戏魔 2019-03-13
牵马的蜘蛛 2018-01-13
凌云 2018-11-26
吟风 2017-05-07
白诗秀儿 2017-03-20
吟风 2016-10-12
小鱼儿 2018-08-17
95崔国权(mobei) 2025-04-02
95崔国权(mobei) 2025-04-02
95崔国权(mobei) 2025-04-02
藏家519 2025-04-03
马踏飞燕 2025-04-03
1 2025-04-03
彭于晏东北分晏 2025-04-02
藏家574 2025-04-02
一耶暴富 2025-04-02
藏家197 2025-04-02