Spring Boot定时任务应用实践

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

一、Spring定时器

1、cron表达式方式

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

上述代码中,在一个类上添加@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>

2、配置cron表达式

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

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

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

3、添加定时任务实现

任务1:

任务2:

定时发送邮件任务:

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;


@DisallowConcurrentExecutionpublic 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方法即可。

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

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

@Configurationpublic 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;

@Configurationpublic 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调度的定时任务就实现好了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 在spring boot中,简单的任务调度工作可以由@Scheduled注解来完成。 如果需要深入了解spring...
    大浪滔滔阅读 2,734评论 0 0
  • 博客原文 徒手翻译spring framework 4.2.3官方文档的第33章,若有翻译不当之处请指正。 定时任...
    rabbitGYK阅读 5,643评论 4 24
  • 参考springBoot中@Scheduled执行原理解析 spring在初始化bean后,通过“postProc...
    谁在烽烟彼岸阅读 835评论 0 0
  • 我的孩子太调皮怎么办?我的老公不关心我怎么办?我的工作太多没时间学习怎么办?我们似乎陷入一个又一个的问题漩涡...
    曼呼吸阅读 136评论 2 4