springboot 之集成quartz

前言

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

环境准备

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

开始集成

  1. pom.xml中增加依赖包


    依赖包.png
        
            org.springframework.boot
            spring-boot-starter-quartz
        
  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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
  • #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触发
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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;

/**
 * 类功能描述:
*
    *
  • 类功能描述1
    *
  • 类功能描述2
    *
  • 类功能描述3
    *
* 修改记录:
*
    *
  • 修改记录描述1
    *
  • 修改记录描述2
    *
  • 修改记录描述3
    *
* * @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

你可能感兴趣的:(springboot 之集成quartz)