springboot 之集成quartz

前言

一直没机会做spring生态圈的框架,公司选择的是一些小众的微服务,鉴于此考虑,丰富自己的技术栈,花了两天时间从网上各网站上学习了springboot一些基础知识。
本章只介绍springboot微服务集成quartz,用于项目中用到的一些定时任务,调度任务框架。

环境准备

  • IntelliJ IDEA
  • 前一章中搭建的微服务框架

开始集成

  1. pom.xml中增加依赖包


    依赖包.png
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
  1. quartz的使用分为两种类型,一种为服务启动时定时执行任务,另一种为服务启动后,通过某些操作控制的任务(可以通过操作对其进行停止,删除,启动...)
    2.1.1 先说第一种:


    EnableScheduling.png

    在入口类DemoApplication中启用调度任务:增加注解@EnableScheduling
    2.1.2 在demo包下新建schedule包,用于存放调度任务相关类,在schedule包下新建TestSchedule类:


    TestSchedule.png
package com.example.demo.schedule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * <li>#cron 的表达式:
 *(1)0 0 2 1 * ? *   表示在每月的1日的凌晨2点调整任务
 *
 *(2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业
 *
 *(3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作
 *
 *(4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点
 *
 *(5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时
 *
 *(6)0 0 12 ? * WED    表示每个星期三中午12点
 *
 *(7)0 0 12 * * ?   每天中午12点触发
 *
 *(8)0 15 10 ? * *    每天上午10:15触发
 *
 *(9)0 15 10 * * ?     每天上午10:15触发
 *
 *(10)0 15 10 * * ? *    每天上午10:15触发
 *
 *(11)0 15 10 * * ? 2005    2005年的每天上午10:15触发
 *
 *(12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发
 *
 *(13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发
 *
 *(14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
 *
 *(15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发
 *
 *(16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发
 *
 *(17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发
 *
 *(18)0 15 10 15 * ?    每月15日上午10:15触发
 *
 *(19)0 15 10 L * ?    每月最后一日的上午10:15触发
 *
 *(20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发
 *
 *(21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发
 *
 *(22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-19
 */
@Component
@Slf4j
public class TestSchedule {

    @Scheduled(fixedRate = 10 * 1000, initialDelay = 5000)  //采用间隔调度,每10秒执行一次
    public void runJoba(){ //定义一个执行的任务
        log.info("[*******MyTaskA -- 间隔调度 ******]"+
                new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS").format(new Date()));
    }

    @Scheduled(cron = "*/10 * * * * ?")  //采用间隔调度,每10秒执行一次
    public void runJobb(){ //定义一个执行的任务
        log.info("[*******MyTaskB -- 间隔调度 ******]"+
                new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS").format(new Date()));
    }

}

此类中包含两种定时方式的定时任务(1.每n秒执行一次定时任务,2.按照cron表达式执行任务),使用方法只需要在执行业务的方法前加@Scheduled注解即可,根据不同场景,使用适当的定时方式执行定时任务

2.2.1 在与DemoApplication同级的包下新建一个Schedule的配置类SchedulerAutoConfiguration,用于通过ScheduleFactory生成schedule实例:


SchedulerAutoConfiguration.png
package com.example.demo;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-19
 */
@Configuration
public class SchedulerAutoConfiguration {

    //这个地方如果需要使用自定义的executor,可以在别的地方配置好,然后这里注入
    //@Autowired
    //private ThreadPoolTaskExecutor taskExecutor;


    @Bean(name="SchedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setAutoStartup(true);
        //这里如果不配置任务池,它就会默认加载SimpleThreadPool
        //factory.setTaskExecutor();
        return factory;
    }

    @Bean(name="funnyScheduler")
    public Scheduler scheduler() throws IOException {
        return schedulerFactoryBean().getScheduler();
    }
}

此处定义的Bean funnyScheduler会被后续schedule对象操作类中注入,注入时,名称必须一致
2.2.2 在service包下新建JobScheduleService接口,定义对调度任务的操作抽象方法


JobScheduleService.png
package com.example.demo.service;
import java.util.Date;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-19
 */
public interface JobScheduleService {

    /**
     * 功能描述: 添加简单任务
     * 可以自定义一个任务信息对象,然后从信息对象中获取参数创建简单任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void addSimpleJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime);

    /**
     * 功能描述: 添加定时任务
     * 可以自定义一个任务信息对象,然后从信息对象中获取参数创建定时任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void addCronJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime, String cronExpression);

    /**
     * 功能描述: 修改任务Trigger,即修改任务的定时机制
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void modifyJob(String jobName, String jobGroup, String cronExpression);

    /**
     * 功能描述: 暂停任务,只支持定时任务的暂停,不支持单次任务,单次任务需要interrupt
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void pauseJob(String jobName, String jobGroup);

    /**
     * 功能描述: 从暂停状态中恢复定时任务运行
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void resumeJob(String jobName, String jobGroup);

    /**
     * 功能描述: 删除任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    void deleteJob(String jobName, String jobGroup);

}

2.2.3 在service.impl包下新建JobScheduleService接口的实现类JobScheduleServiceImpl


JobScheduleServiceImpl.png
package com.example.demo.service.impl;
import com.example.demo.service.JobScheduleService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.Date;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-19
 */
@Service
@Slf4j
public class JobScheduleServiceImpl implements JobScheduleService {

    /**
     * 因为在配置中设定了这个bean的名称,这里就需要指定bean的名称,不然启动就会报错
     */
    @Autowired
    @Qualifier("funnyScheduler")
    private Scheduler scheduler;

    /**
     * 功能描述: 添加简单任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void addSimpleJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime) {
        JobDetail jobDetail = JobBuilder.newJob(taskClass).withIdentity(jobName, jobGroup).build();
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName, jobGroup)
                .startAt(startTime)
                .endAt(endTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(3)
                        .withRepeatCount(5))
                .build();
        try {
            scheduler.scheduleJob(jobDetail, simpleTrigger);
        } catch (SchedulerException e) {
            log.error("addSimpleJob catch {}", e.getMessage());
        }
    }

    /**
     * 功能描述: 添加定时任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void addCronJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime, String cronExpression) {
        JobDetail jobDetail = JobBuilder.newJob(taskClass).withIdentity(jobName, jobGroup).build();
        // 触发器

        try {
            CronTrigger trigger = new CronTriggerImpl(jobName, jobGroup, jobName, jobGroup, startTime, endTime, cronExpression);// 触发器名,触发器组
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException | ParseException e) {
            log.error("addCronJob catch {}", e.getMessage());
        }
    }

    /**
     * 功能描述: 修改任务Trigger,即修改任务的定时机制
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void modifyJob(String jobName, String jobGroup, String cronExpression) {
        TriggerKey oldKey = new TriggerKey(jobName, jobGroup);
        //表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
        //按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().
                withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
        try {
            scheduler.rescheduleJob(oldKey, trigger);
        } catch (SchedulerException e) {
            log.error("modifyJob catch {}", e.getMessage());
        }
    }

    /**
     * 功能描述: 暂停任务,只支持定时任务的暂停,不支持单次任务,单次任务需要interrupt
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void pauseJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (StringUtils.isEmpty(jobDetail)) {
                System.out.println("没有这个job");
            }
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            log.error("pauseJob catch {}", e.getMessage());
        }
    }

    /**
     * 功能描述: 从暂停状态中恢复定时任务运行
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void resumeJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            log.error("resumeJob catch {}", e.getMessage());
        }
    }

    /**
     * 功能描述: 删除任务
     *
     * @param
     * @return:void
     * @since: v1.0
     * @Author:xf
     * @Date: 2019/3/15 17:00
     */
    @Override
    public void deleteJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.error("deleteJob catch {}", e.getMessage());
        }
    }
}

此类实现了JobScheduleService 接口定义的对调度任务各种操作的具体实现步骤,通过注入funnyScheduler来操作。
2.2.4 定义了对调度任务的操作类后,需要增加自己的调度任务业务实现类,也就是任务具体要干的事,需要实现quartz中的Job接口,并重写其execute方法,在其中增加自己的业务流程,在schedule包中新建job包,并在job包下新建SimpleJob类:


SimpleJob.png
package com.example.demo.schedule.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-19
 */
@Slf4j
public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("简单任务执行中");
    }
}

2.2.5 最后这个任务是怎么被调用的呢?其实在任何地方,业务执行过程中,rest接口中,都可以对这个任务进行CUD操作:


scheduleSimpleJob.png
package com.example.demo.controller;
import com.example.demo.schedule.job.SimpleJob;
import com.example.demo.service.JobScheduleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;

/**
 * 类功能描述:<br>
 * <ul>
 * <li>类功能描述1<br>
 * <li>类功能描述2<br>
 * <li>类功能描述3<br>
 * </ul>
 * 修改记录:<br>
 * <ul>
 * <li>修改记录描述1<br>
 * <li>修改记录描述2<br>
 * <li>修改记录描述3<br>
 * </ul>
 *
 * @author xuefl
 * @version 5.0 since 2019-12-18
 */
@RestController
@Api(value = "SwaggerValue", tags={"SwaggerController"},description = "swagger应用",  produces = MediaType.APPLICATION_JSON_VALUE)
public class SimpleController {
    @Resource
    private JobScheduleService jobScheduleService;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    @ApiOperation(value="hello",httpMethod = "GET",notes="hello",produces = MediaType.APPLICATION_JSON_VALUE)
    public String sayHello() {
        return "hello world";
    }

    @RequestMapping(value = "/scheduleSimpleJob", method = RequestMethod.POST)
    @ApiOperation(value="scheduleSimpleJob",httpMethod = "POST",notes="scheduleSimpleJob",produces = MediaType.APPLICATION_JSON_VALUE)
    public void scheduleSimpleJob() {
        jobScheduleService.addSimpleJob(SimpleJob.class,
                "simpleJob", "simpleJob", new Date(), null);
    }

}

注入jobScheduleService对象后 增加一个接口/scheduleSimpleJob,增加其实现业务方法,通过jobScheduleService对象对调度任务进行管理,此处只以addSimpleJob为例,其他方法大家可以增加rest接口以测试

  1. 启动后运行日志如下


    调度任务线程池实例化.png

    第一种定时任务运行日志.png

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

推荐阅读更多精彩内容

  • quartz的介绍就不说了,这里主要说的是使用spring boot+2.2.1搭建集群。 1.pom引入 2.在...
    jack_520阅读 5,228评论 0 4
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 一点知识 在JAVA开发领域,目前可以通过以下几种方式进行定时任务: Timer:jdk中自带的一个定时调度类,可...
    雅倩兰爸爸阅读 3,827评论 1 28
  • 什么是定时任务调度 基于给定的时间点,给定的时间间隔或者给定的执行次数自动完成执行任务 在Java中的定时调度工具...
    Hey_Shaw阅读 2,476评论 2 1
  • Quartz是一个完全由java编写的功能丰富的开源作业调度库,可以集成到几乎任何Java应用程序中,小到独立应用...
    ProteanBear阅读 6,985评论 3 24