springboot整合quartz实现定时任务动态增删改查

news2024/11/24 15:01:28

springboot整合quartz

  • quartz介绍
    • Quartz 核心概念
  • 使用
    • 依赖
    • 配置文件
    • 启动类
    • quartz工具类
    • job业务
    • 用到的对象类
    • 接口
    • service
    • 实现类
    • 测试

quartz介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。
中文官方文档:https://www.w3cschool.cn/quartz_doc/

Quartz 核心概念

1.Job 表示一个工作,要执行的具体内容。
2.JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
3.Trigger 代表一个调度参数的配置,什么时候去调。
4.Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。

使用

依赖

springboot整合quartz依赖

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

pom文件

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
    </parent>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

配置文件

quartz必须配置,如果在application.yml文件配置了quartz属性,则不需要quartz.properties文件。
application.yml文件

server:
  port: 8802
spring:
  application:
    name: quartz-curd
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/quartz_curd?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  quartz:
    #quartz定时任务,采用数据库方式
    job-store-type: jdbc
    initialize-schema: never
    #定时任务启动开关,true-false-关
    auto-startup: true
    #延迟1秒启动定时任务
    startup-delay: 1s
    #启动时更新己存在的Job
    overwrite-existing-jobs: true
    properties:
      org:
        quartz:
          scheduler:
            instanceName: MyScheduler
            #ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
            instanceId: AUTO
          jobStore:
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            #StdJDBCDelegate说明支持集群
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            #quartz内部表的前缀
            tablePrefix: QRTZ_
            #是否加入集群
            isClustered: false
            #容许的最大作业延长时间
            misfireThreshold: 12000
            #分布式节点有效性检查时间间隔,单位:毫秒
            clusterCheckinInterval: 15000
            #配置是否使用
            useProperties: false
          threadPool:
            #ThreadPool实现的类名
            class: org.quartz.simpl.SimpleThreadPool
            #线程数量
            threadCount: 20
            #线程优先级
            threadPriority: 5
            #配置是否启动自动加载数据库内的定时任务,默认true
            threadsInheritContextClassLoaderOfInitializingThread: true

注意:initialize-schema: never,这个属性,第一次生成quartz内置表时设置为always,生成后改为never,不然每次启动系统都会重新生成表。

启动类

@SpringBootApplication
@EnableOpenApi
public class QuartzService {
    public static void main(String[] args) {
        SpringApplication.run(QuartzService.class,args);
    }
}

添加启动类后可以运行项目,生成quartz内置表,
生成11张表
在这里插入图片描述

quartz工具类

包含定时任务添加,修改,运行,暂停,恢复,删除和查询。

package com.test.quartz.utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.quartz.pojo.JobInfo;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.spi.MutableTrigger;

import java.util.*;

import static org.quartz.Trigger.TriggerState.*;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-08 14:41
 */
@Slf4j
public class QuartzUtil {

    private static Map<Trigger.TriggerState, String> TRIGGER_STATE;

    public static final String END_JOB_SUFFIX = ":endTask";

    static {
        TRIGGER_STATE = new HashMap<>();
        TRIGGER_STATE.put(NONE, "空");
        TRIGGER_STATE.put(NORMAL, "正常");
        TRIGGER_STATE.put(PAUSED, "暂停");
        TRIGGER_STATE.put(COMPLETE, "完成");
        TRIGGER_STATE.put(ERROR, "错误");
        TRIGGER_STATE.put(BLOCKED, "阻塞");
    }

    /**
     * 创建定时任务,创建定时任务后默认为启动状态
     * @param scheduler 调度器
     * @param jobInfo 定时任务信息类
     * 原文链接:https://blog.csdn.net/yyyy11119/article/details/121139580
     */
    public static void createScheduleJob(Scheduler scheduler, JobInfo jobInfo)throws Exception{
        //获取定时任务的执行类,必须是类的绝对路径
        //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
        Class<? extends Job> jobClass = (Class<? extends Job>)Class.forName(jobInfo.getJobClassName());
        //构建定时任务信息
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(),jobInfo.getJobGroup());
        TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(),jobInfo.getJobGroup());
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                .withIdentity(jobKey).build();
        //设置定时任务的执行方式
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression());
        //构建触发器trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withDescription(jobInfo.getDescription())
                .usingJobData("jobClassName",jobInfo.getJobClassName())
                .withSchedule(scheduleBuilder)
                .startAt(jobInfo.getStartTime())
                //如果设置了结束时间,到期任务自动删除,我们不想任务过期就删除,所以zhudiao
                //.endAt(jobInfo.getEndTime())
                .build();
        // 修改 misfire 策略,如果开始时间在添加任务之前,不修改策略会导致新增时任务会立即运行一次
        chgMisFire(trigger);
        //如果参数不为空,写入参数
        ObjectMapper mapper = new ObjectMapper();
        if (!StringUtils.isBlank(jobInfo.getParameter())) {
            Map param = mapper.readValue(jobInfo.getParameter(), Map.class);
            jobDetail.getJobDataMap().putAll(param);
        }
        scheduler.scheduleJob(jobDetail,trigger);
        //如果设置了结束时间,添加一个终止任务
        if (null != jobInfo.getEndTime()){
            addEndJob(scheduler,jobInfo);
        }
    }

    /**
     * 添加停止任务
     * @param scheduler 调度器
     * @param jobInfo 定时任务信息类
     */
    public static void addEndJob(Scheduler scheduler,JobInfo jobInfo)throws SchedulerException,JsonProcessingException,ClassNotFoundException{
        String jobName = jobInfo.getJobName();
        String group = jobName + END_JOB_SUFFIX;
        JobKey jobKey = JobKey.jobKey(jobName,group);
        String endJobName = "jobName-" + System.currentTimeMillis();
        TriggerKey triggerKey = TriggerKey.triggerKey(endJobName,group);
        if (scheduler.checkExists(triggerKey) && scheduler.checkExists(jobKey)) {
            log.info("{}----开始添加终止任务",jobName);
            SimpleTrigger trigger = (SimpleTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withDescription(jobInfo.getDescription() + "-[结束任务]")
                    .withIdentity(triggerKey)
                    .startAt(jobInfo.getEndTime()).build();
            // 修改 misfire 策略
            chgMisFire(trigger);
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            ObjectMapper mapper = new ObjectMapper();
            if (!StringUtils.isBlank(jobInfo.getParameter())) {
                Map param = mapper.readValue(jobInfo.getParameter(), Map.class);
                jobDetail.getJobDataMap().putAll(param);
            }
            scheduler.deleteJob(jobKey);
            scheduler.scheduleJob(jobDetail, trigger);
            return;
        }
        Class<? extends Job> jobClass = (Class<? extends Job>)Class.forName(jobInfo.getJobClassName());
        //构建定时任务信息
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                .withIdentity(jobKey).build();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
                .withDescription(jobKey.getGroup() + "-[结束任务]")
                .startAt(jobInfo.getEndTime())
                .build();
        // 修改 misfire 策略
        chgMisFire(trigger);

        ObjectMapper mapper = new ObjectMapper();
        if (!StringUtils.isBlank(jobInfo.getParameter())) {
            log.info("parameter:{}",jobInfo.getParameter());
            Map param = mapper.readValue(jobInfo.getParameter(), Map.class);
            jobDetail.getJobDataMap().putAll(param);
        }
        scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 根据任务明细暂停定时任务
     * @param scheduler 调度器
     * @param jobName 定时任务名称
     */
    public static void pauseScheduleJob(Scheduler scheduler,String jobName,String jobGroup)throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 根据任务名称恢复定时任务
     * @param scheduler 调度器
     * @param jobName 任务名称
     */
    public static void resumeScheduleJob(Scheduler scheduler,String jobName,String jobGroup)throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        scheduler.resumeJob(jobKey);
    }

    /**
     * 根据任务名称运行一次定时任务
     * @param scheduler 调度器
     * @param jobName 任务名称
     */
    public static void runOnce(Scheduler scheduler,String jobName,String jobGroup)throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新定时任务
     * @param scheduler 调度器
     * @param jobInfo 定时任务类
     */
    public static void updateScheduleJob(Scheduler scheduler,JobInfo jobInfo)throws SchedulerException,JsonProcessingException,ClassNotFoundException{
        //获取对应任务的触发器
        TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getJobName(),jobInfo.getJobGroup());
        //设置定时任务执行方式
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression());
        //重新构建定时任务的触发器
        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        cronTrigger = cronTrigger.getTriggerBuilder()
                .withIdentity(triggerKey)
                .withSchedule(scheduleBuilder)
                .withDescription(jobInfo.getDescription())
                .usingJobData("jobClassName",jobInfo.getJobClassName())
                .startAt(jobInfo.getStartTime())
                .endAt(jobInfo.getEndTime()).build();
        // 修改 misfire 策略,测试时注掉,方便观察
        //chgMisFire(trigger);
        //重置对应的定时任务
        //如果参数不为空,写入参数
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(),jobInfo.getJobGroup());
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        ObjectMapper mapper = new ObjectMapper();
        if (!StringUtils.isBlank(jobInfo.getParameter())) {
            Map param = mapper.readValue(jobInfo.getParameter(), Map.class);
            jobDetail.getJobDataMap().clear();
            jobDetail.getJobDataMap().putAll(param);
        }
        scheduler.rescheduleJob(triggerKey,cronTrigger);
        //如果设置了结束时间,添加一个终止任务
        if (null != jobInfo.getEndTime()){
            addEndJob(scheduler,jobInfo);
        }
    }

    /**
     * 删除定时任务
     * @param scheduler 调度器
     * @param jobName 任务名称
     */
    public static void deleteScheduleJob(Scheduler scheduler,String jobName,String jobGroup)throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 查询所有定时任务
     * @param scheduler 调度器
     */
    public static List<JobInfo> findAll(Scheduler scheduler,String jobGroup)throws SchedulerException{
        GroupMatcher<JobKey> matcher;
        if (StringUtils.isBlank(jobGroup)){
            matcher = GroupMatcher.anyGroup();
        }else {
            matcher = GroupMatcher.groupEquals(jobGroup);
        }

        List<JobInfo> list = new ArrayList<>();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey:jobKeys){
            JobInfo jobInfo = new JobInfo();
            jobInfo.setJobName(jobKey.getName());
            jobInfo.setJobGroup(jobKey.getGroup());
            TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(),jobKey.getGroup());
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //终止任务没有设置业务实现类
            if (!jobKey.getGroup().contains(END_JOB_SUFFIX)){
                jobInfo.setJobClassName(cronTrigger.getJobDataMap().getString("jobClassName"));
                System.out.println("className:" + scheduler.getJobDetail(jobKey).getJobDataMap().getString("jobClassName"));
                jobInfo.setDescription(cronTrigger.getDescription());
                jobInfo.setCronExpression(cronTrigger.getCronExpression());
                jobInfo.setStartTime(cronTrigger.getStartTime());
            }

            jobInfo.setJobStatus(getJobStatus(scheduler,triggerKey));
            JobDataMap map = scheduler.getJobDetail(jobKey).getJobDataMap();
            jobInfo.setParameter(JSONObject.toJSONString(map));

            //直接在cronTrigger设置结束时间,到期后会自动删除,我们用结束时间设置了一个定时任务,到期停止
            //jobInfo.setEndTime(cronTrigger.getEndTime());
            TriggerKey endJobTriggerKey = TriggerKey.triggerKey(jobKey.getName(),jobKey.getName() + END_JOB_SUFFIX);
            SimpleTrigger endJobTrigger = (SimpleTrigger) scheduler.getTrigger(endJobTriggerKey);
            if (null != endJobTrigger){
                jobInfo.setEndTime(endJobTrigger.getStartTime());
            }
            list.add(jobInfo);
        }
        return list;
    }

    /**
     * 获取定时任务状态
     * @param scheduler 调度器
     * @param triggerKey 触发器
     * @return
     * @throws SchedulerException
     */
    public static String getJobStatus(Scheduler scheduler,TriggerKey triggerKey) throws SchedulerException{
        Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
        return TRIGGER_STATE.get(triggerState);
    }

    /**
     * 错过触发策略:即错过了定时任务的开始时间可以做什么,此处设置什么也不做,即过了开始时间也不执行
     * 如果不设置,默认为立即触发一次,例如设置定时任务开始时间为10点,没有设置结束时间,在10点后重启系统,
     * 如果不设置为CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING,那么开始时间在重启前的定时任务会全部重新触发一次
     * @param trigger
     */
    private static void chgMisFire(Trigger trigger) {
        // 修改 misfire 策略
        if (trigger instanceof MutableTrigger) {
            MutableTrigger mutableTrigger = (MutableTrigger) trigger;
            mutableTrigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
        }
    }
}

注意在添加和修改定时任务时,结束时间的设置一定要注意,直接设置结束时间,到期后任务会自动删除
在这里插入图片描述

job业务

具体让job执行什么操作,都可以在这里实现,个人只是打印了任务名称和参数

package com.test.quartz.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.test.quartz.mapper.TriggersMapper;
import com.test.quartz.pojo.Triggers;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static com.test.quartz.utils.QuartzUtil.END_JOB_SUFFIX;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-08 16:07
 */
@Slf4j
public class MyJob extends QuartzJobBean {

    @Autowired
    private TriggersMapper triggersMapper;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        //获取参数信息
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String name = jobDetail.getKey().getName();
        String jobGroup = jobDetail.getKey().getGroup();
        Iterator<Map.Entry<String, Object>> iterator = jobDataMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, Object> entry = iterator.next();
            log.info("定时任务-{}:{}-启动,参数为:key:{} ,value:{}",name,jobGroup,entry.getKey(),entry.getValue());
        }

        Trigger cronTrigger = jobExecutionContext.getTrigger();
        String group = cronTrigger.getKey().getGroup();
        //有终止任务标记的修改状态为pause
        if (group.contains(END_JOB_SUFFIX)){
            String jobName = group.split(END_JOB_SUFFIX)[0];
            log.info("jobName:{}",jobName);
            QueryWrapper<Triggers> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("job_name",jobName);
            List<Triggers> triggers = triggersMapper.selectList(queryWrapper);
            for (Triggers trigger:triggers){
                triggersMapper.updateTriggerState("PAUSED",trigger.getJobName(),trigger.getJobGroup());
            }
            return;
        }
    }
}

用到的对象类

package com.test.quartz.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-08 14:29
 */
@Data
@TableName("tb_job_info")
public class JobInfo{

    //任务类名
    private String jobClassName;

    //cron表达式
    private String cronExpression;

    //参数
    private String parameter;

    //任务名称
    private String jobName;

    //任务分组
    private String jobGroup;

    //描述
    private String description;

    //任务状态
    private String jobStatus;

    //任务开始时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date startTime;

    //任务结束时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date endTime;

}

job_info表根据自己需求建立,本项目中只用于对象传输。建表sql如下。
注意:我建表使用utf8mb4编码,数据库也得是utf8mb4才行,如果数据库是utf8,建表编码也改成utf8。

CREATE TABLE `tb_job_info` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
  `create_at` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(32) DEFAULT NULL COMMENT '修改人',
  `update_at` datetime DEFAULT NULL COMMENT '修改时间',
  `del_flag` int(11) DEFAULT '0' COMMENT '删除状态:0-未删除,1-已删除',
  `job_class_name` varchar(255) DEFAULT NULL COMMENT '任务类名',
  `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
  `parameter` varchar(255) DEFAULT NULL COMMENT '参数',
  `job_name` varchar(20) DEFAULT NULL COMMENT '任务类名',
  `job_group` varchar(20) DEFAULT NULL COMMENT '任务分组',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `job_status` int(11) DEFAULT NULL COMMENT '任务状态',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='任务信息表'

vo对象

@Data
public class OperateVo {
    private String jobName;

    private String jobGroup;

    private Integer operateType;
}

用到的枚举

package com.test.quartz.enums;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-09 15:16
 */
public enum OperateEnum {
    ADD(1,"添加"),
    UPDATE(2,"修改"),
    RUN_ONCE(3,"运行一次"),
    PAUSE(4,"暂停"),
    RESUME(5,"恢复"),
    DELETE(6,"删除");

    private Integer code;

    private String msg;

    OperateEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

接口

编写接口测试

package com.test.quartz.controller;

import com.test.quartz.pojo.JobInfo;
import com.test.quartz.service.JobService;
import com.test.quartz.vo.OperateVo;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-08 18:20
 */
@RestController
@RequestMapping("job")
@Api(tags = "定时任务操作")
public class JobController {

    @Autowired
    private JobService jobService;

    @GetMapping("findAll")
    public ResponseEntity findAll(String jobGroup){
        return jobService.findAll(jobGroup);
    }

    @PostMapping("save")
    public ResponseEntity save(@RequestBody JobInfo jobInfo,Integer operateType){
        return jobService.save(jobInfo,operateType);
    }

    @PostMapping("operate")
    public ResponseEntity operate(@RequestBody OperateVo vo){
        return jobService.operate(vo);
    }
}


service

public interface JobService {
    ResponseEntity findAll(String jobGroup);

    ResponseEntity save(JobInfo jobInfo,Integer operateType);

    ResponseEntity operate(OperateVo vo);
}

实现类

package com.test.quartz.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.test.quartz.enums.OperateEnum;
import com.test.quartz.job.MyJob;
import com.test.quartz.pojo.JobInfo;
import com.test.quartz.service.JobService;
import com.test.quartz.utils.QuartzUtil;
import com.test.quartz.vo.OperateVo;
import jdk.nashorn.internal.runtime.logging.Logger;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronExpression;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @author清梦
 * @site www.xiaomage.com
 * @company xxx公司
 * @create 2023-06-08 18:24
 */
@Service
@Slf4j
public class JobServiceImpl implements JobService {

    @Autowired
    private Scheduler scheduler;

    @Override
    public ResponseEntity findAll(String jobGroup) {
        List<JobInfo> list = new ArrayList<>();
        try {
            list = QuartzUtil.findAll(scheduler,jobGroup);
        }catch (SchedulerException e){
            log.error("查询定时任务列表失败:{}",e);
            return ResponseEntity.ok("查询失败");
        }catch (Exception e){
            log.error("查询定时任务列表失败:{}",e);
            return ResponseEntity.ok("查询失败");
        }
        return ResponseEntity.ok(list);
    }

    @Override
    public ResponseEntity save(JobInfo jobInfo,Integer operateType){
        String msg = "";
        try {
            //校验cron是否有效
            if (!CronExpression.isValidExpression(jobInfo.getCronExpression())){
                return ResponseEntity.ok("cron表达式错误");
            }

            if (OperateEnum.ADD.getCode() == operateType){
                msg = OperateEnum.ADD.getMsg();
                QuartzUtil.createScheduleJob(scheduler,jobInfo);
            }else if (OperateEnum.UPDATE.getCode() == operateType){
                msg = OperateEnum.UPDATE.getMsg();
                QuartzUtil.updateScheduleJob(scheduler,jobInfo);
            }

        }catch (Exception e){
            log.error("定时任务-{}-失败,jobName:{},错误信息:{}",msg ,jobInfo.getJobName(),e);
            return ResponseEntity.ok("定时任务" + msg + "失败");
        }
        return ResponseEntity.ok("定时任务" + msg + "成功");
    }

    @Override
    public ResponseEntity operate(OperateVo vo) {
        String jobName = vo.getJobName();
        String jobGroup = vo.getJobGroup();
        Integer type = vo.getOperateType();
        String msg = "";
        try {
            if (OperateEnum.RUN_ONCE.getCode() == type){
                msg = OperateEnum.RUN_ONCE.getMsg();
                QuartzUtil.runOnce(scheduler,jobName,jobGroup);
            }else if (OperateEnum.PAUSE.getCode() == type){
                msg = OperateEnum.PAUSE.getMsg();
                QuartzUtil.pauseScheduleJob(scheduler,jobName,jobGroup);
            }else if (OperateEnum.RESUME.getCode() == type){
                msg = OperateEnum.RESUME.getMsg();
                QuartzUtil.resumeScheduleJob(scheduler,jobName,jobGroup);
            }else if (OperateEnum.DELETE.getCode() == type){
                msg = OperateEnum.DELETE.getMsg();
                QuartzUtil.deleteScheduleJob(scheduler,jobName,jobGroup);
            }
        }catch (Exception e){
            log.error("定时任务:{}-{}失败:{}",msg,e);
            return ResponseEntity.ok("定时任务:" + jobName + msg + "-失败");
        }
        return ResponseEntity.ok("定时任务:" + jobName + msg + "-成功");
    }
}


测试

因为没有前端页面,我们引入了swagger,运行项目后在浏览器输入
http://localhost:8802/swagger-ui/,出现页面如下
在这里插入图片描述
进入定时任务操作接口,先测试添加和修改
在这里插入图片描述

输入参数,点击执行
在这里插入图片描述

添加成功后可以在查询接口查看,不输入任务分组时可以看到设置的定时终止任务
在这里插入图片描述
参考博客:
https://blog.csdn.net/yyyy11119/article/details/121139580
本博客源码地址:
https://gitee.com/qfp17393120407/springboot_quartz.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/639364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CMake 入门与进阶(6)】 CMakeLists.txt 部分常用变量(附使用代码)

变量也是 cmake 中的一个重头戏&#xff0c;cmake 提供了很多内置变量&#xff0c;每一个变量都有它自己的含义&#xff0c;通过这个链接地址cmake-variables(7) — CMake 3.5.2 Documentation可以查询到所有的内置变量及其相应的介绍&#xff0c;如下所示&#xff1a; 在这一份…

App移动端测试 —— Monkey的日志

Monkey的日志对于分析脚本的执行情况十分必要。 Monkey 日志由以下几部分组成&#xff1a; 测试命令信息&#xff1a;随机种子 seed、运行次数、可运行应用列表、各事件百分比。” 正文内容从这里开始&#xff08;可直接省略&#xff0c;亦可配图说明&#xff09;。 01—Mon…

使用python批量处理工作簿和工作表

案例01 批量新建并保存工作簿 将for语句与xlwings模块相结合&#xff0c;就可以轻松实现批量创建工作簿 import xlwings as xw # 导入xlwings模块 app xw.App(visibleTrue,add_bookFalse) # 启动Excel程序&#xff0c;但不新建工作簿 for i in range(6):workbook app.books…

Pyinstaller 快速找出所需库的方法

目录 【auto-py-to-exe】第三方库 使用【auto-py-to-exe】逐步尝试 1.什么文件都不添加&#xff0c;选择项目与单目录后直接打包 2.直接运行生成的exe文件 3.定位所需的包 4.--add-data 和 --hidden-import 打包为一个exe文件 【auto-py-to-exe】第三方库 其是pyinstaller操…

【头歌-Python】9.1 X射线衍射曲线绘制(project)第3~4关

第3关&#xff1a;X 射线衍射曲线峰值 任务描述 本关任务&#xff1a;读文件中的数据绘制线图形并加绘制峰值。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; python 读取文件中的数据使用 matplotlib 绘制图形 python 读取文件 python读取文件可以用以…

6.volatile与JMM

volatile与JMM 6.1 被volatile修饰的变量有两大特点 特点&#xff1a; 可见性 有序性&#xff1a;有排序要求&#xff0c;有时需要禁重排 内存语义&#xff1a; 当写一个volatile变量时&#xff0c;JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中 当读一个vol…

Linux:ftp服务 (vsftpd)

我的ftp服务器为centos7 使用yum安装的vsftpd软件 &#xff08;如果你需要离线yum库&#xff0c;而不会搭建可以查看&#xff09; Linux&#xff1a;rpm查询安装 && yum安装_鲍海超-GNUBHCkalitarro的博客-CSDN博客 安装软件 配置环境 其次是Linux的防火墙和selinu…

写一个Orillusion编辑器(伪)

界面效果 思路 bootstrap控制界面效果jquery动态修改界面内容[Add]增加一个box[Play]导出play.html,打开play.html就可以看到程序运行效果 编辑器代码 <!DOCTYPE html> <html> <!-- TODO 1. 序列化数据,保存工程 2. 反序列化数据,打开工程 --><head>…

Python实现ACO蚁群优化算法优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蚁群优化算法(Ant Colony Optimization, ACO)是一种源于大自然生物世界的新的仿生进化算法&#xff0c…

腾讯安全SOC+荣获“鑫智奖”,助力金融业数智化转型

近日&#xff0c;由金科创新社主办&#xff0c;全球金融专业人士协会支持的“2023鑫智奖第五届金融数据智能优秀解决方案评选”榜单正式发布。腾讯安全申报的“SOC基于新一代安全日志大数据平台架构的高级威胁安全治理解决方案”获评“鑫智奖网络信息安全创新优秀解决方案”。 …

大数据分析平台释疑专用帖

大数据分析平台是大数据时代&#xff0c;企业数字化运营决策的一大关键平台&#xff0c;但初次接触大数据分析平台时&#xff0c;大家必然是问题多多的&#xff0c;本文将就几个比较常见的问题进行总结回答&#xff0c;希望对正在了解、选型大数据分析平台的大家有所帮助。 首…

Qt学习08:文本输入类与快捷键

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 QLineEditQTextEditQTextCursor QPlainTextQKeySequenceEdit QLineEdit QLineEdit是最基本的输入控件&#xff0c;常用于短行的文本输入。 构造函数 QLineEdit(const QString &contents, QWidget *parent …

07_scrapy的应用——获取电影数据(通过excel保存静态页面scrapy爬虫数据的模板/通过数据库保存)

0、前言: 一般我们自己创建的一些python项目,我们都需要创建虚拟环境,其中会下载很多包,也叫做依赖。但是我们在给他人分享我们的项目时,不能把虚拟环境打包发送给别人,因为每个人电脑系统不同,我们可以把依赖导出为依赖清单,然后别人有了我们的依赖清单,就可以用一条…

[网络工程师]-网络规划与设计-网络测试运行和维护(二)

4、局域网测试 局域网测试主要是检验网络是否为应用系统提供了稳定、高效的网络平台,如果网络系统不够稳定,网络应用就不可能快速稳定。对于常规的以太网进行系统测试,主要包括系统连通性、链路传输速率、吞吐率、传输时延、丢包率及链路层健康状况测试等基本功能测试。 4.…

用那种方式安装 ThinkPHP 5.0?

简单介绍 ThinkPHP是一个免费开源的&#xff0c;快速、简单的面向对象的轻量级PHP开发框架&#xff0c;是为了敏捷WEB应用开发和简化企业应用开发而诞生的。 ThinkPHP5.0版本是一个颠覆和重构版本&#xff0c;采用全新的架构思想&#xff0c;引入了更多的PHP新特性&#xff0c…

分享几个免费 AI 生成工具 (第一期)

今天来给大家分享几个国外免费的AI视频生成工具 Artflow ai https://app.artflow.ai Artflow.ai 是一款人工智能工具&#xff0c;旨在帮助用户创建自定义头像并让他们的故事栩栩如生。主要特点和优势包括&#xff1a; 自定义创作&#xff1a;从文本中生成角色、场景和声音&…

利用C语言实现十大经典排序算法的方法

排序算法 算法分类 —— 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a;通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。 非比较类排序&#xff1a;不通过比较来决定元素间…

《实战AI低代码》:一场新的软件工程革命

目录 五十年前的软件危机 二十年前的软件危机 今天的软件危机 革命性的新技术 AI低代码开发宣言 AI低代码开发原则 一场新的软件工程革命 AI低代码开发不仅是继面向过程,面向对象之后的一种新的抽象方式,也是继瀑布开发,敏捷开发之后的一种新的开发方法。 五十年前的…

QT学习07:五种按钮控件

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 抽象类&#xff1a;QAbstractButtonQPushButtonQToolButtonQCommandLinkButtonQRadioButtonQCheckBoxQButtonGroup 抽象类&#xff1a;QAbstractButton 是所有按钮类的祖先。 QAbstractButton的信号&#xff1a…

【appium】appium自动化入门之API(中)

上一篇介绍了在appium上启动app和两个定位元素的小工具使用方法&#xff0c;这篇就从appium的一种小方法&#xff1a;远程控制&#xff0c;以及如何把appium上的输入法调整成中文&#xff0c;算是两个小技巧吧 目录 正文 2.4 Remote 远程控制 前言 2.4.1 设置 IP 2.4.2 访问地址…