Spring Boot定时任务应用实践

作者: JeffWong

在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



原文创作:JeffWong

原文链接:https://www.cnblogs.com/jeffwongishandsome/p/spring-boot-integrate-task-schedule-practise.html

更多推荐

更多
  • Spring Boot实战-00开篇词从零开始:为什么要学习SpringBoot? 你为什么需要学习这门课程?,这门课程是如何设计的?,讲师寄语, 你好,我是鉴湘,有 10 年以上大型 Java EE 和分布架进行系统开发和维护有着丰富的实践经验。 在我的从业生涯中,曾经带过不少项目,以我所带领的电商项目开发团队为例
  • Spring Boot实战-04定制配置:如何创建和管理自定义的配置信息? 如何在应用程序中嵌入系统配置信息?,如何创建和使用自定义配置信息?,如何组织和整合配置信息?,如何覆写内置的配置类?,小结与预告,03 讲中,我们介绍了 Spring Boot ...
  • Spring Boot实战-09数据抽象:SpringData如何对数据访问过程进行统一抽象? Repository 接口及实现,多样化查询支持,Spring Data 中的组件,小结与预告, 事实上,JdbcTemplate 是相对偏底层的一个工具类,作为系统开发最重要的基础功能之一,族中另一个重要成员 Spring Data
  • Spring Boot实战-13服务调用:如何正确理解RestTemplate远程调用实现原理? 初始化 RestTemplate 实例,RestTemplate 核心执行流程,从源码解析到日常开发,小结与预告, 在 12 讲中,我们详细描述了如何使用 RestTemplate 访问 HTTP 端点的使用方法,它涉及 Res中的这些
  • Spring Boot实战-12服务调用:如何使用RestTemplate消费RESTful服务? 使用 RestTemplate 访问 HTTP 端点,实现 SpringCSS 案例中的服务交互,小结与预告,11 讲我们介绍了如何使用 Spring Boot 构建 RESTful 风格 Web 服务的实现方法,而 S 服务的构建后,
  • Spring Boot实战-结束语以终为始:SpringBoot总结和展望 Spring Boot 的创新性,Spring Boot 课程总结,Spring Boot 的发展和演进, 终于到了课程的最后一讲啦,这一讲我们将对整个 Spring Boot 课程进行总结。 Spring Boring Boot 提供
  • Spring Boot实战-08数据访问:如何剖析JdbcTemplate数据访问实现原理? 从模板方法模式和回调机制说起,JDBC API 到 JdbcTemplate 的演变,JdbcTemplate 源码解析,从源码解析到日常开发,小结与预告,07 讲中,我们介绍了使用 JdbcTemplate ...
  • Spring Boot实战-03多维配置:如何使用SpringBoot中的配置体系? 创建第一个 Spring Boot Web 应用程序,Spring Boot 中的配置体系,小结与预告, 配置体系是基于 Spring Boot 框架开发应用程序的基础,而自动配置也是该框架的核心功能之一。今eb 应用程序开始吧。 创
  • Spring Boot实战-21指标定制:如何实现自定义度量指标和Actuator端点? Actuator 中的度量指标,自定义 Actuator 端点,小结与预告,20 讲中我们引入了 Spring Boot Actuator 组件来满足 这一讲我们继续讨论如何扩展 Actuator 端点,但更多关注与度量指标相关的内容
  • Spring Boot实战-02案例驱动:如何剖析一个SpringWeb应用程序? Spring MVC VS Spring Boot,剖析一个 Spring Web 应用程序,案例驱动:SpringCSS,小结与预告, 在 01 讲中,我们提到 Spring 家族具备很多款开源框架,开发人员可以基于这些开发框架实现各种 ...
  • 近期文章

    更多
    文章目录

      推荐作者

      更多