SpingBoot2.X+Quartz实现任务调度

SpringBoot任务调度之-Quartz

在SpringBoot中实现定时任务,我常用的有以下两种:

  • SpringTask:spring自主研发的定时任务工具,并且存在于Spring体系中,不需要添加任何依赖。
  • Quartz:是一个开源的任务调度服务,几乎可以集成到任务Java应用程序中 昨天已经介绍了springTask,

今天来介绍Quartz,他是一个强大的开源任务调度工具,可以实现复杂定时任务,使用SprngBoot+Mybatis+Quartz+Swagger实现定时任务的增删改查,启用和停用

设计Meaven

  • Mybatis(使用的数据据版本是8.0)使用5.7的会有问题
  • Quartz
  • Swagger

Quartz介绍

Quartz时一个开源的定时任务项目,可以单独使用也可以和应用程序结合使用。Quartz可是常见简单Jobs进行运行,也可以运行上万个复杂的Jobs。

Quartz核心API

  • Job(任务):是一个接口,里面有一个方法,可以通过实现Job接口定义需要执行的任务Job接口如下:
  package org.quartz;

  public interface Job {

    public void execute(JobExecutionContext context)
      throws JobExecutionException;
  }
  • JobDetail:用于创建Job实例
 public class TestJob implements Job {

    public TestJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println(" TestJob is start executing.");
    }
  }
 
  public class Test {
    public static void main(String[] args) throws SchedulerException {
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .build();
      }
      }
 
  • Trigger:触发器,描述触发Job执行的时间触发规则实现类SimpleTrigger和CronTrigger可以通过crom表达式定义出各种复杂的调度方案
  • JobBuilder:用于定义JobDetail实例,用于定义Job实例
  • TriggerBuilder:用于构建Trigger实例
  • Scheduler(调度):表示一个独立的Quartz容器,Trigger和JobDetail可以注册到Scheduler中。

代码实现

添加依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--集成druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>  8.0.16</version>
        </dependency>
        <!--MyBatis Plus 依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!--MyBatis Plus 代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!--Velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>swagger-models</artifactId>
                    <groupId>io.swagger</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>swagger-annotations</artifactId>
                    <groupId>io.swagger</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--解决Swagger 2.9.2版本NumberFormatException-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.0</version>
        </dependency>

        <!--Hutool Java工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.5.7</version>
        </dependency>
    </dependencies>

在application.yml配置Quartz

:本文介绍的是定时任务持久化到数据库中,因此(具体可参考官网)

  • jobStore:class: org.quartz.impl.jdbcjobstore.JobStoreTX
server:
  port: 9999
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: zbb920604
  quartz:
    properties:
      org:
        quartz:
          scheduler:
            instanceName: clusteredScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: qrtz_
            isClustered: true
            clusterCheckinInterval: 10000
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
    job-store-type: jdbc

#mybatis-plus:
#  mapper-locations: classpath*:/mapper/**/**Mapper.xml

mybatis:
  mapper-locations:
    - classpath:mapper/*.xml
    - classpath*:com/**/mapper/*.xml

建立将定时任务持久化到数据库所需要的表

定时任务所需要的表实际上已经在Quartz依赖中路径如下:

  • \org\quartz\impl\jdbcjobstore

Quartz表介绍

表名 简介 QRTZ_CALENDARS 以 Blob 类型存储 Quartz 的 Calendar 信息 QRTZ_CRON_TRIGGERS 存储 Cron Trigger,包括 Cron表达式和时区信息 QRTZ_FIRED_TRIGGERS 存储与已触发的 Trigger 相关的状态信息,以及相联 Job的执行信息 QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的 Trigger 组的信息 QRTZ_SCHEDULER_STATE 存储少量的有关 Scheduler 的状态信息,和别的 Scheduler实例(假如是用于一个集群中) QRTZ_LOCKS 存储程序的悲观锁的信息(假如使用了悲观锁) QRTZ_JOB_DETAILS 存储每一个已配置的 Job 的详细信息 QRTZ_JOB_LISTENERS 存储有关已配置的 JobListener 的信息 QRTZ_SIMPLE_TRIGGERS 存储简单的Trigger,包括重复次数,间隔,以及已触的次数 QRTZ_BLOG_TRIGGERS Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) QRTZ_TRIGGER_LISTENERS 存储已配置的 TriggerListener 的信息 RTZ_TRIGGERS 存储已配置的 Trigger 的信息

添加输入参数类

package com.cn.greemes.quartzdemo.dto;

import lombok.Data;

/**
*
*/
@Data
public class QuartzVo {
    /**
    * 定时任务类全称
    */
    private String jobClassName;
    /**
    * 定时任务所属组
    */
    private String jobGroupName;
    /**
    * cron 表达式
    */
    private String cron;
}

添加配置

@Configuration
public class SchedulerConfig implements SchedulerFactoryBeanCustomizer {

    @Autowired
    private DataSource dataSource;

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        try {
            System.out.println(dataSource.getConnection());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        // 启动延时
        schedulerFactoryBean.setStartupDelay(10);
        // 自动启动任务调度
        schedulerFactoryBean.setAutoStartup(true);
        // 是否覆盖现有作业定义
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 配置数据源
        schedulerFactoryBean.setDataSource(dataSource);
    }

}

添加Job

  • BaseJob
package com.cn.greemes.quartzdemo.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public interface BaseJob extends Job {
    @Override
    void execute(JobExecutionContext context) throws JobExecutionException;
}
  • MyJob用于测试
package com.cn.greemes.quartzdemo.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;
import java.util.List;

public class MyJob implements BaseJob{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        System.out.println("myjob任务开始");
        System.out.println("定时任务执行时间"+new Date());
    }

}

在Controller层添加增删改查

package com.cn.greemes.quartzdemo.model.quartz.controller;


import com.cn.greemes.quartzdemo.Job.BaseJob;
import com.cn.greemes.quartzdemo.common.CommonResult;
import com.cn.greemes.quartzdemo.common.ResultCode;
import com.cn.greemes.quartzdemo.dto.QuartzVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.print.DocFlavor;
import java.util.Date;
@Controller
@Api(tags = "JobController", description = "任务管理")
@RequestMapping("/Job")
public class JobController {

    private static final Logger LOGGER = LoggerFactory.getLogger(JobController.class);


    @Autowired
    Scheduler scheduler;


    /**
    * 新增job任务
    * @param quartzVo
    * @return
    * @throws Exception
    */
    @ApiOperation(value = "新增Job服务")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult addJob(@RequestBody QuartzVo quartzVo) throws Exception {
        String jobName = quartzVo.getJobClassName();
        String cron = quartzVo.getCron();
        String jobGroupName = quartzVo.getJobGroupName();

        //校验cron表达式 是否符合规范
        boolean b = CronExpression.isValidExpression(cron);
        if (!b){
            return CommonResult.failed("cron表达式书写有误,请重新提交");
        }
        // 启动调度器
        scheduler.start();
        //构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(jobName, jobGroupName).build();
        //cron表达式调度器构建
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        //构建 Trigger
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(scheduleBuilder).build();
        Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
        if (date==null){
            return CommonResult.failed("添加定时任务失败");
        }
        return CommonResult.success(ResultCode.SUCCESS);
    }

    @ApiOperation(value = "修改Job服务")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult updateJob(@RequestBody QuartzVo quartzVo) throws SchedulerException {
        String jobName = quartzVo.getJobClassName();
        String cron = quartzVo.getCron();
        String jobGroupName = quartzVo.getJobGroupName();
        //校验cron表达式 是否符合规范
        boolean b = CronExpression.isValidExpression(cron);
        if (!b){
            return CommonResult.failed("cron表达式书写有误,请重新提交");
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        //重新构建表达式trigger
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        Date date = scheduler.rescheduleJob(triggerKey, trigger);
        if (date==null){
            return CommonResult.failed("添加定时任务失败");
        }
        return CommonResult.success(ResultCode.SUCCESS);

    }




    @ApiOperation(value = "删除Job服务")
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult deleteJob(@RequestBody QuartzVo quartzVo) throws SchedulerException {

        String jobName = quartzVo.getJobClassName();
        String jobGroup = quartzVo.getJobGroupName();
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        scheduler.pauseTrigger(triggerKey);
        scheduler.unscheduleJob(triggerKey);
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        boolean deleteJob = scheduler.deleteJob(jobKey);
        if (!deleteJob){
            return CommonResult.failed("删除定时任务失败");
        }
        return CommonResult.success(ResultCode.SUCCESS);
    }

    @ApiOperation(value = "启动定时任务Job服务")
    @RequestMapping(value = "/start", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult startJob(@RequestBody QuartzVo quartzVo) throws SchedulerException {
        String jobClassName = quartzVo.getJobClassName();
        String jobGroupName = quartzVo.getJobGroupName();
        scheduler.resumeJob(JobKey.jobKey(jobClassName,jobGroupName));
        return CommonResult.success(ResultCode.SUCCESS);
    }


    @ApiOperation(value = "暂停Job服务")
    @RequestMapping(value = "/stop", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult stopJob(@RequestBody QuartzVo quartzVo) throws SchedulerException {
        String jobClassName = quartzVo.getJobClassName();
        String jobGroupName = quartzVo.getJobGroupName();
        scheduler.pauseJob(JobKey.jobKey(jobClassName,jobGroupName));
        return CommonResult.success(ResultCode.SUCCESS);
    }



    private BaseJob getClass(String jobName) throws Exception {
        String name = "com.cn.greemes.quartzdemo.Job."+jobName;
        Class<?> class1 = Class.forName(name);
        return (BaseJob) class1.newInstance();
    }

}

总结:

今天介绍了定时任务中的一种Quartz定时任务持久化到数据库,网上有很多介绍Quartz的但是她们都不是已经SpringBoot2.X的,本文是根据我最近工作使用,进行总结所得,希望对大家有所帮助。想获取源码的转发文章关注我,获取源码

喜欢我的可以进行关注,谢谢

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容