在Spring Boot中实现定时任务功能,可以通过Spring自带的定时任务调度,也可以通过集成经典开源组件Quartz实现任务调度。

一、Spring定时器

1、cron表达式方式

使用自带的定时任务,非常简单,只需要像下面这样,加上注解就好,不需要像普通定时任务框架那样继承任何定时处理接口 ,简单示例代码如下:

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()))); } }

SpringTaskA

上述代码中,在一个类上添加@EnableScheduling注解,在方法上加上@Scheduled,配置下cron表达式,一个最最简单的cron定时任务就完成了。cron表达式的各个组成部分,可以参考下面:

@Scheduled(cron = "[Seconds] [Minutes] [Hours] [Day of month] [Month] [Day of week] [Year]")

2、fixedRate和fixedDelay

@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())));
} }

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、添加依赖

        <dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>

quartz

2、配置cron表达式

示例代码需要,在application.properties文件中新增如下配置:

## Quartz定时job配置
job.taska.cron=*/3 * * * * ?
job.taskb.cron=*/7 * * * * ?
job.taskmail.cron=*/5 * * * * ?

cron-config

其实,我们完全可以不用配置,直接在代码里面写或者持久化在DB中然后读取也可以。

3、添加定时任务实现

任务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()))); }
}

QuartzTaskA

任务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()))); }
}

QuartzTaskB

定时发送邮件任务:

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));
} }
}

MailSendTask

实现任务看上去非常简单,继承Quartz的Job接口,重写execute方法即可。

4、集成Quartz定时任务

怎么让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;
}
}

AutowireBeanJobFactory

定义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;
} }

QuartzConfig

接着配置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;
} }

TaskSetting

最后启动你的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

Spring Boot定时任务应用实践的更多相关文章

  1. Spring Boot 2 (五):Docker Compose + Spring Boot + Nginx + Mysql 实践

    Spring Boot 2 (五):Docker Compose + Spring Boot + Nginx + Mysql 实践 Spring Boot + Nginx + Mysql 是实际工作中 ...

  2. Spring Boot Admin最佳实践

    本文不进行Spring Boot Admin入门知识点说明 在Spring Boot Actuator中提供很多像health.metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标.Spr ...

  3. Spring Boot缓存应用实践

    缓存是最直接有效提升系统性能的手段之一.个人认为用好用对缓存是优秀程序员的必备基本素质. 本文结合实际开发经验,从简单概念原理和代码入手,一步一步搭建一个简单的二级缓存系统. 一.通用缓存接口 1.缓 ...

  4. spring boot.定时任务问题记录(TaskScheduler/ScheduledExecutorService异常)

    一.背景 spring boot的定时任务非常简单,只需要在启动类中加上@EnableScheduling注解,然后在对应的方法上配置@Scheduled就可以了,系统会自动处理并按照Schedule ...

  5. (14)Spring Boot定时任务的使用【从零开始学Spring Boot】

    本文介绍在 Spring Boot 中如何使用定时任务,使用非常简单,就不做过多说明了. com.kfit.base.scheduling.SchedulingConfig: package com. ...

  6. Spring Boot 定时任务单线程和多线程

    Spring Boot 的定时任务: 第一种:把参数配置到.properties文件中: 代码: package com.accord.task; import java.text.SimpleDat ...

  7. Spring Boot (十一): Spring Boot 定时任务

    在实际的项目开发工作中,我们经常会遇到需要做一些定时任务的工作,那么,在 Spring Boot 中是如何实现的呢? 1. 添加依赖 在 pom.xml 文件中只需引入 spring-boot-sta ...

  8. Spring Boot 定时任务 @Scheduled

    项目开发中经常需要执行一些定时任务,比如在每天凌晨,需要从 implala 数据库拉取产品功能活跃数据,分析处理后存入到 MySQL 数据库中.类似这样的需求还有许多,那么怎么去实现定时任务呢,有以下 ...

  9. Spring Boot定时任务运行一段时间后自动关闭的解决办法

    用Spring Boot默认支持的 Scheduler来运行定时任务,有时在服务器运行一段时间后会自动关闭.原因:Schedule默认是单线程运行定时任务的,即使是多个不同的定时任务,默认也是单线程运 ...

随机推荐

  1. Download all Apple open source OS X files at once

    While it is well known that Mac OS X contains open source code, how to access and download that sour ...

  2. 3.QT中QCommandLineParser和QCommandLineOption解析命令行参数

     1  新建项目 main.cpp #include <QCoreApplication> #include <QCommandLineParser> #include & ...

  3. CSDN专访:大数据时代下的商业存储

    原文地址:http://www.csdn.net/article/2014-06-03/2820044-cloud-emc-hadoop 摘要:EMC公司作为全球信息存储及管理产品方面的领先公司,不久 ...

  4. 后端分布式系列:分布式存储-HDFS DataNode 设计实现解析

    前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...

  5. 03安卓TextView

    一  TextView    父类 : View     >概念:文本控件 :文本内容的显示   默认配置不可编辑  子类EditText可以编辑 *********************** ...

  6. install_driver(Oracle) failed: Can't load `.../DBD/Oracle/Oracle.so' for module DBD::Oracle

    Description This section is from the "Practical mod_perl " book, by Stas Bekman and Eric C ...

  7. 精通CSS+DIV网页样式与布局--制作实用菜单

    在上篇博文中,小编中主要的简单总结了一下CSS中关于如何设置页面和浏览器元素,今天小编继续将来介绍CSS的相关基础知识,这篇博文,小编主要简单的总结一下在CSS中如何制作网页中的菜单,这部分的内容包括 ...

  8. 【UNIX网络编程第三版】阅读笔记(一):代码环境搭建

    粗略的阅读过<TCP/IP详解>和<计算机网络(第五版)>后,开始啃这本<UNIX网络编程卷一:套接字联网API>,目前linux下的编程不算太了解,在阅读的过程中 ...

  9. pig简单的代码实例:报表统计行业中的点击和曝光量

    注意:pig中用run或者exec 运行脚本.除了cd和ls,其他命令不用.在本代码中用rm和mv命令做例子,容易出错. 另外,pig只有在store或dump时候才会真正加载数据,否则,只是加载代码 ...

  10. Java中的五种单例模式

    Java模式之单例模式: 单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例. 特点: 1,一个类只能有一个实例 2 自己创建这个实例 3 整个系统都要使用这个实例 例: 在下面 ...