SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(动态新增、修改等操作)

news2024/10/10 5:34:38

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(动态新增、修改等操作)

  • 前言
  • 数据库脚本
  • 创建需要被调度的方法
  • 创建相关实体类
  • 创建业务层接口
  • 创建业务层实现类
  • 控制层类
  • 测试结果

前言

我这边的SpringBoot的版本为2.6.13,其中Quartz是使用的以下方式引入

<!--quartz定时任务-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

数据库脚本

首先确定maven拉取了 spring-boot-starter-quartz 的依赖,再接着到私仓下面找到
"你的私仓地址\org\quartz-scheduler\quartz\2.3.2"把这个下面的quartz-2.3.2.jar 给解压
然后到这个文件的 “quartz-2.3.2\org\quartz\impl\jdbcjobstore” 下面就可以可以看到

在这里插入图片描述
我这边选择的是“tables_mysql_innodb.sql”脚本
内容如下,执行以后会出现11张表

#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(190) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(190) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(190) NULL,
JOB_GROUP VARCHAR(190) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit;

在这里插入图片描述

表的说明

表名称说明
qrtz_blob_triggersblog类型存储triggers
qrtz_calendars以blog类型存储Calendar信息
qrtz_cron_triggers存储cron trigger信息
qrtz_fired_triggers存储已触发的trigger相关信息
qrtz_job_details存储每一个已配置的job details
qrtz_locks存储悲观锁的信息
qrtz_paused_trigger_grps存储已暂停的trigger组信息
qrtz_scheduler_state存储Scheduler状态信息
qrtz_simple_triggers存储simple trigger信息
qrtz_simprop_triggers存储其他几种trigger信息
qrtz_triggers存储已配置的trigger信息

创建需要被调度的方法

package com.example.springbootfull.quartztest;

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @DisallowConcurrentExecution : 此标记用在实现Job的类上面,意思是不允许并发执行.
 * 因为Quartz的是并发操作的,要非常注意线程不安全,最好加上这个注解,以防止导致业务数据错乱
 */
@Slf4j
@DisallowConcurrentExecution
public class BookTask extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        JobDetail jobDetail = context.getJobDetail();
        log.info("book定时任务-开始执行,"
                + "任务名:" + jobDetail.getKey().getName()
                + ",组名:" + jobDetail.getKey().getGroup()
                + " "
                + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        log.info("book定时任务-执行结束,"
                + "任务名:" + jobDetail.getKey().getName()
                + ",组名:" + jobDetail.getKey().getGroup()
                + " "
                + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }
}

创建相关实体类

package com.example.springbootfull.quartztest.bean;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

// 使用@Data注解自动为该类生成getter、setter、equals、hashCode和toString方法
@Data
public class QuartzBean {

    // 任务名称
    private String jobName;

    // 任务组的名称
    private String jobGroupName;

    // 任务的状态(例如:运行中、已停止等)
    private String state;

    // 执行任务的类名(通常是一个实现了Job接口的类的全限定名)
    private String jobClass;

    // 间隔单位(例如:秒、分钟、小时等)
    private String intervalUnit;

    // 间隔单位的可读名称(例如:Seconds、Minutes、Hours等)
    private String intervalUnitName;

    // 时间间隔的数量(例如:如果intervalUnit是Minutes,这个值可能是5,表示每5分钟执行一次)
    private Integer timeInterval;

    // Cron表达式,用于更复杂的任务调度(例如:每天凌晨1点执行)
    private String cronExpression;

    // 任务开始时间,使用@DateTimeFormat和@JsonFormat注解来指定日期格式和时区
    // @DateTimeFormat用于Spring MVC绑定表单数据时解析日期
    // @JsonFormat用于JSON序列化/反序列化时处理日期
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date startTime;

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

    // 任务的描述信息
    private String description;
}

创建业务层接口

package com.example.springbootfull.quartztest.service;

import com.example.springbootfull.quartztest.bean.QuartzBean;
import com.example.springbootfull.quartztest.zdyresult.ResponseResult;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;

import java.util.List;


public interface IQuartzService {


    /**
     * 添加定时任务信息
     *
     * @param quartzBean 定时任务信息
     */
    void save(QuartzBean quartzBean);

    /**
     * 移除定时任务--根据任务名称移除
     *
     * @param jobName 任务名
     */
    void delete(String jobName);

    /**
     * 移除定时任务
     *
     * @param groupName 组名
     * @param jobName   任务名
     */
    void delete(String jobName, String groupName);

    /**
     * 修改定时任务
     *
     * @param quartzBean 任务信息
     */
    void update(QuartzBean quartzBean);

    /**
     * 添加任务
     *
     * @param jobName 任务名
     * @return 任务信息
     */
    QuartzBean selectByName(String jobName);

    /**
     * 查询单个定时任务信息
     *
     * @param groupName 组名称
     * @param jobName   任务名称
     * @return 查询结果
     */
    QuartzBean selectByName(String jobName, String groupName);


    /**
     * 查询定时任务列表
     *
     * @param quartzBean 查询条件
     * @return 查询结果
     */
    List<QuartzBean> selectList(QuartzBean quartzBean);

    /**
     * 暂停定时任务
     *
     * @param jobName 任务名
     */
    void pause(String jobName);

    /**
     * 暂停定时任务
     *
     * @param jobName   任务名
     * @param groupName 组名
     */
    void pause(String jobName, String groupName);

    /**
     * 恢复定时任务
     *
     * @param jobName 任务名
     */
    void resume(String jobName);

    /**
     * 恢复定时任务
     *
     * @param jobName   任务名
     * @param groupName 组名
     */
    void resume(String jobName, String groupName);

    /**
     * 执行定时任务
     *
     * @param jobName 任务名
     */
    void executeJob(String jobName);

    /**
     * 执行定时任务
     *
     * @param jobName   任务名
     * @param groupName 组名
     */
    void executeJob(String jobName, String groupName);
}

创建业务层实现类

package com.example.springbootfull.quartztest.serviceImpl;

import com.example.springbootfull.quartztest.bean.QuartzBean;
import com.example.springbootfull.quartztest.service.IQuartzService;
import lombok.SneakyThrows;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

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

@Service
public class QuartzServiceImpl implements IQuartzService {

    @Resource
    private Scheduler scheduler;


    /**
     * 保存一个 Quartz 作业到调度器中。
     *
     * @param quartzBean 包含作业和触发器配置信息的 QuartzBean 对象。
     * @throws SchedulerException     如果在调度作业或触发器时发生错误。
     * @throws ClassNotFoundException 如果找不到指定的作业类。
     */
    @Override
    @SneakyThrows
    public void save(QuartzBean quartzBean) {
        // 通过反射获取作业类的 Class 对象
        Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
        // 获取作业名称
        String jobName = quartzBean.getJobName();
        // 获取作业组名称,如果没有指定则使用默认组
        String jobGroupName = !StringUtils.hasText(quartzBean.getJobGroupName()) ? Scheduler.DEFAULT_GROUP : quartzBean.getJobGroupName();
        // 获取触发器组名称,通常与作业组名称相同
        String triggerGroupName = !StringUtils.hasText(quartzBean.getJobGroupName()) ? Scheduler.DEFAULT_GROUP : quartzBean.getJobGroupName();
        // 获取作业开始时间,如果没有指定则使用当前时间
        Date startTime = quartzBean.getStartTime() == null ? new Date() : quartzBean.getStartTime();
        // 获取作业结束时间
        Date endTime = quartzBean.getEndTime();
        // 获取作业描述,如果没有指定则使用空字符串
        String description = !StringUtils.hasText(quartzBean.getDescription()) ? "" : quartzBean.getDescription();
        // 构建作业详情对象
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                .withIdentity(jobName, jobGroupName) // 设置作业名称和组名称
                .withDescription(description)        // 设置作业描述
                .build();
        // 检查是否指定了 Cron 表达式
        if (quartzBean.getCronExpression() != null && quartzBean.getCronExpression().length() > 0) {
            // 如果指定了 Cron 表达式,则构建基于 Cron 的触发器
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(jobName, triggerGroupName) // 设置触发器名称和组名称
                    .startAt(startTime)                      // 设置触发器开始时间
                    .endAt(endTime)                          // 设置触发器结束时间(可选)
                    .withSchedule(CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression())
                            .withMisfireHandlingInstructionDoNothing()) // 设置 Cron 计划和误触发处理策略
                    .build();
            // 将作业和触发器添加到调度器中
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // 如果没有指定 Cron 表达式,则构建基于日历间隔的触发器
            DateBuilder.IntervalUnit cycleUnit = DateBuilder.IntervalUnit.valueOf(quartzBean.getIntervalUnit());
            Integer timeInterval = quartzBean.getTimeInterval();

            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(jobName, triggerGroupName) // 设置触发器名称和组名称
                    .startAt(startTime)                      // 设置触发器开始时间
                    .endAt(endTime)                          // 设置触发器结束时间(可选)
                    .withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule()
                            .withInterval(timeInterval, cycleUnit) // 设置间隔计划和单位
                            .withMisfireHandlingInstructionDoNothing()) // 设置误触发处理策略
                    .build();
            // 将作业和触发器添加到调度器中
            scheduler.scheduleJob(jobDetail, trigger);
        }
    }

    /**
     * 移除定时任务--根据任务名称移除
     */
    @Override
    public void delete(String jobName) {
        delete(jobName, Scheduler.DEFAULT_GROUP);
    }

    /**
     * 移除定时任务
     */
    @Override
    @SneakyThrows
    public void delete(String jobName, String groupName) {
        groupName = !StringUtils.hasText(groupName) ? Scheduler.DEFAULT_GROUP : groupName;

        // 指定作业(Job)
        JobKey jobKey = new JobKey(jobName, groupName);
        // 指定触发器
        TriggerKey triggerKey = new TriggerKey(jobName, groupName);
        // 使用scheduler对象暂停与指定TriggerKey相关联的触发器
        scheduler.pauseTrigger(triggerKey);
        // 使用scheduler对象暂停与指定JobKey相关联的作业。
        scheduler.pauseJob(jobKey);
        // 移除触发器
        scheduler.unscheduleJob(triggerKey);
        // 删除任务
        scheduler.deleteJob(jobKey);

    }


    /**
     * 修改定时任务
     */
    @Override
    @SneakyThrows
    public void update(QuartzBean quartzBean) {
        delete(quartzBean.getJobName());
        save(quartzBean);
    }

    /**
     * 查询单个定时任务
     */
    @Override
    public QuartzBean selectByName(String jobName) {
        return selectByName(jobName, Scheduler.DEFAULT_GROUP);
    }

    /**
     * 查询单个定时任务
     */
    @Override
    @SneakyThrows
    public QuartzBean selectByName(String jobName, String groupName) {
        groupName = !StringUtils.hasText(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        QuartzBean quartzBean = new QuartzBean();
        JobKey jobKey = new JobKey(jobName, groupName);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        quartzBean.setJobName(jobName);
        quartzBean.setJobGroupName(groupName);
        setJob(jobKey, quartzBean, jobDetail);
        return quartzBean;
    }

    /**
     * 查询定时任务列表
     */
    @Override
    @SneakyThrows
    public List<QuartzBean> selectList(QuartzBean quartzBean) {
        List<QuartzBean> quartzBeans = new ArrayList<>();
        GroupMatcher<JobKey> mathcher = GroupMatcher.anyJobGroup();
        String keyWord = quartzBean.getJobName();
        Set<JobKey> jobKeys = scheduler.getJobKeys(mathcher);
        if (jobKeys==null) {
            return new ArrayList<>();
        }
        for (JobKey jobKey : jobKeys) {
            if (StringUtils.hasText(keyWord) && !jobKey.getName().contains(keyWord)) {
                continue;
            }
            QuartzBean quartzBean2 = new QuartzBean();
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            quartzBean2.setJobName(jobKey.getName());
            quartzBean2.setJobGroupName(jobKey.getGroup());
            List<? extends Trigger> triggers = setJob(jobKey, quartzBean2, jobDetail);
            quartzBean2.setState(scheduler.getTriggerState(triggers.get(0).getKey()).name());
            quartzBeans.add(quartzBean2);
        }
        return quartzBeans;
    }

    /**
     * 暂停定时任务
     */
    @Override
    public void pause(String jobName) {
        pause(jobName, Scheduler.DEFAULT_GROUP);
    }

    /**
     * 暂停定时任务
     */
    @Override
    @SneakyThrows
    public void pause(String jobName, String groupName) {
        groupName = !StringUtils.hasText(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        TriggerKey triggerKey = new TriggerKey(jobName, groupName);
        scheduler.pauseTrigger(triggerKey);
        JobKey jobKey = new JobKey(jobName);
        scheduler.pauseJob(jobKey);

    }

    /**
     * 恢复定时任务
     */
    @Override
    public void resume(String jobName) {
        resume(jobName, Scheduler.DEFAULT_GROUP);
    }


    /**
     * 恢复定时任务
     */
    @Override
    @SneakyThrows
    public void resume(String jobName, String groupName) {
        //检查groupName是否有有效的文本内容,没有有效的则用默认的组名
        groupName = !StringUtils.hasText(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        TriggerKey triggerKey = new TriggerKey(jobName, groupName);
        scheduler.resumeTrigger(triggerKey);
        JobKey jobKey = new JobKey(jobName);
        scheduler.resumeJob(jobKey);

    }


    /**
     * 执行定时任务
     */
    @Override
    public void executeJob(String jobName) {
        executeJob(jobName, Scheduler.DEFAULT_GROUP);
    }


    /**
     * 执行定时任务
     */
    @Override
    @SneakyThrows
    public void executeJob(String jobName, String groupName) {
        //检查groupName是否有有效的文本内容,没有有效的则用默认的组名
        groupName = !StringUtils.hasText(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        JobKey jobKey = new JobKey(jobName, groupName);
        scheduler.triggerJob(jobKey);
    }


    /**
     * 根据提供的JobKey和JobDetail信息,设置QuartzBean的属性,并返回与该作业关联的触发器列表。
     *
     * @param jobKey      作业的唯一标识,包括作业名称和作业组名。
     * @param quartzBean  用于封装Quartz作业相关属性的对象。
     * @param jobDetail   包含作业详细信息的JobDetail对象。
     * @return 与指定作业关联的触发器列表,列表中的触发器类型可能是CronTrigger或CalendarIntervalTrigger等。
     * @throws SchedulerException 如果在获取触发器列表时发生错误。
     */
    private List<? extends Trigger> setJob(JobKey jobKey, QuartzBean quartzBean, JobDetail jobDetail) throws SchedulerException {
        // 设置作业类名和描述
        quartzBean.setJobClass(jobDetail.getJobClass().getName());
        quartzBean.setDescription(jobDetail.getDescription());
        // 尝试获取与指定作业关联的触发器列表
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        // 检查触发器列表是否为空或没有元素
        if (triggers == null || triggers.isEmpty()) {
            // 根据业务需求,可以选择抛出异常、返回空列表或进行其他处理
            // 这里为了简化,直接返回空列表(注意:这可能会隐藏潜在的问题)
            return Collections.emptyList();
        }
        // 获取第一个触发器(注意:这里假设至少有一个触发器)
        Trigger trigger = triggers.get(0);
        // 设置触发器的开始时间和结束时间
        quartzBean.setStartTime(trigger.getStartTime());
        quartzBean.setEndTime(trigger.getEndTime());
        // 根据触发器的具体类型设置相应的属性
        if (trigger instanceof CronTrigger) {
            // 如果是CronTrigger类型,则获取Cron表达式
            CronTrigger cronTrigger = (CronTrigger) trigger; // 使用instanceof后可以直接强制转换,无需再次检查类型
            quartzBean.setCronExpression(cronTrigger.getCronExpression());
        } else if (trigger instanceof CalendarIntervalTrigger) {
            // 如果是CalendarIntervalTrigger类型,则获取重复间隔单位和重复间隔时间
            CalendarIntervalTrigger calendarIntervalTrigger = (CalendarIntervalTrigger) trigger;
            quartzBean.setIntervalUnit(calendarIntervalTrigger.getRepeatIntervalUnit().toString());
            quartzBean.setTimeInterval(calendarIntervalTrigger.getRepeatInterval());
        }
        // 注意:这里只处理了两种类型的触发器,如果有其他类型的触发器,需要添加相应的处理逻辑
        // 返回与指定作业关联的触发器列表
        return triggers;
    }

}

控制层类

package com.example.springbootfull.quartztest.controller;

import com.example.springbootfull.quartztest.bean.QuartzBean;
import com.example.springbootfull.quartztest.service.IQuartzService;
import com.example.springbootfull.quartztest.zdyresult.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;


@RestController
@RequestMapping("taskScheduler")
public class QuartzController {

    @Autowired
    private IQuartzService quartzService;

    /**
     * 添加定时任务信息
     *
     * @param quartzBean 定时任务信息
     * @return ReturnModel 添加定时任务
     */
    @PostMapping(value = "save")
    public ResponseResult<String> save(@RequestBody QuartzBean quartzBean) {
// {
//     "jobName": "bookTask",
//     "description": "书籍定时任务",
//     // "startTime": "2024-01-12 15:20:00",
//     // "endTime": "2024-01-13 00:00:00",
//     "jobClass": "com.example.springbootfull.quartztest.BookTask",
//     "cronExpression": "*/10 * * * * ?"
// }
        quartzService.save(quartzBean);
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 移除定时任务
     *
     * @param quartzBean 定时任务信息
     * @return ReturnModel 移除定时任务
     */
    @PostMapping(value = "/delete")
    public ResponseResult<String> delete(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName": "bookTask"
//        }
        quartzService.delete(quartzBean.getJobName());
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 修改定时任务
     *
     * @param quartzBean 定时任务信息
     * @return ReturnModel 修改定时任务
     */
    @PostMapping(value = "update")
    public ResponseResult<String> update(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName":"bookTask",
//                "description":"1",
//                "jobTypeRadio":"expression",
//                "startTime":"2024-01-13 14:00:00",
//                "endTime":"",
//                "jobClass":"com.example.demo.system.controller.BookTask",
//                "cronExpression":"*/30 * * * * ?"
//        }
        quartzService.update(quartzBean);
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 暂停定时任务
     *
     * @param quartzBean 定时任务名称
     * @return ReturnModel 暂停定时任务
     */
    @PostMapping(value = "pause")
    public ResponseResult<String> pause(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName": "bookTask2"
//        }
        quartzService.pause(quartzBean.getJobName());
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 恢复定时任务
     *
     * @param quartzBean 定时任务名称
     * @return ReturnModel 恢复定时任务
     */
    @PostMapping(value = "resume")
    public ResponseResult<String> resume(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName": "bookTask2"
//        }
        quartzService.resume(quartzBean.getJobName());
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 执行定时任务
     *
     * @param quartzBean 定时任务名称
     * @return ReturnModel 执行定时任务
     */
    @PostMapping(value = "executeJob")
    public ResponseResult<String> executeJob(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName": "bookTask2"
//        }
        quartzService.executeJob(quartzBean.getJobName());
        return ResponseResult.success(quartzBean.getJobName());
    }

    /**
     * 查询单个定时任务信息
     *
     * @param jobName 任务名称
     * @return ReturnModel 查询单个定时任务信息
     */
    @GetMapping(value = "selectByName")
    public ResponseResult<QuartzBean> selectByName(@RequestParam("jobName") String jobName) {
        QuartzBean quartzBean = quartzService.selectByName(jobName);
        return ResponseResult.success(quartzBean);
    }

    /**
     * 查询定时任务列表
     *
     * @param quartzBean 查询条件
     * @return ReturnModel 查询定时任务列表
     */
    @PostMapping(value = "selectList")
    public ResponseResult<List<QuartzBean>> selectList(@RequestBody QuartzBean quartzBean) {
//        {
//            "jobName": ""
//        }
        List<QuartzBean> quartzBeans = quartzService.selectList(quartzBean);
        return ResponseResult.success(quartzBeans);
    }
}

测试结果

我这边只测试一下查看
在这里插入图片描述

参考文章
【1】【QUARTZ】springboot+quartz动态配置定时任务

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

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

相关文章

Android 防止截屏和录屏

通过给当前的window对象设置标记WindowManager.LayoutParams.FLAG_SECURE来防止截屏和录屏 protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 防止截屏getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManage…

vscode配置:启用括号对着色

想要的效果&#xff1a;启用括号对着色&#xff0c;在大括号之间用折线表示&#xff0c;看起来会更加直观方便&#xff0c;例如在less中嵌套层级比较多时&#xff0c;大括号的层级不容易看清楚&#xff0c;做了这个配置会更好一些。 vscode安装扩展插件&#xff1a;Bracket P…

Spring Boot学习资源库:Spring生态的精华

摘 要 社会的进步&#xff0c;教育行业发展迅速&#xff0c;人们对教育越来越重视&#xff0c;在当今网络普及的情况下&#xff0c;教学模式也开始逐渐网络化&#xff0c;各大高校开始网络教学模式。 本文研究的教学资源库系统基于Springboot框架&#xff0c;采用Java技术和MYS…

Linux deepin系统通过编辑crontab来设置定时任务---定时关机

在Linux系统中&#xff0c;crontab 是用来设置周期性被执行的指令的守护进程。通过编辑 crontab&#xff0c;您可以安排定时任务&#xff0c;比如定时关机、定时备份文件、定时运行脚本等。以下是如何编辑 crontab 来设置定时任务的步骤&#xff1a; 打开终端&#xff1a;您可以…

AcWing 802. 区间和(离散化算法,python)

本篇博客详细讲解一下离散化知识点&#xff0c;通过讲解和详细列题带大家掌握离散化。 题目&#xff1a; 原题链接&#xff1a;https://www.acwing.com/problem/content/description/804/ 假定有一个无限长的数轴&#xff0c;数轴上每个坐标上的数都是 0。 现在&#xff0c;…

平时使用的正则总结

1、将某一个字符串的后缀名后面加上“!400_500” 使用场景是将minio拿过来的图片压缩尺寸从而压缩其大小&#xff0c;加快渲染的速度。需要在图片的后缀名后面加上尺寸如下&#xff1a; const str //storage-test.test.shiqiao.com/gateway/common/isopen/2024/10/09/e708e9…

科创集团所属园区入驻企业北京铭镓半导体获 “硬科技”潜在独角兽企业认定

近日&#xff0c;科创集团所属工宇园区企业北京铭镓半导体荣获北京市科委、中关村管委会“硬科技”潜在独角兽企业认定。独角兽企业特指具备显著创新力、展现出强劲成长潜力以及获得市场高度认可的企业&#xff0c;是新经济领域发展的标志性存在。 北京铭镓半导体有限公司于202…

如何搭建直播美颜平台?视频美颜SDK的核心技术详解

时下&#xff0c;美颜效果作为提升直播吸引力的重要手段&#xff0c;已经成为主播和观众的共同期待。本篇文章&#xff0c;小编将与大家分享搭建一个高效的直播美颜平台的流程&#xff0c;重点介绍视频美颜SDK的核心技术。 一、直播美颜平台的构建 搭建一个直播美颜平台&#…

vue3:自定义描点定位组件(锚点定位和监听滚动切换)以及遇到的问题

目录 第一章 实现效果 第二章 锚点组件分析 2.1 功能分析 2.2 核心点 第三章 源代码 3.1 数据格式 3.2 代码分析 3.2.1 tab栏以及内容页面 3.2.2 逻辑 第四章 遇到的问题 第一章 实现效果 第二章 锚点组件分析 2.1 功能分析 tab栏以及切换涉及逻辑点击tab切换同时页…

uni-app 打包成app时 限制web-view大小

今天对接一个uni-app的app 内置对方h5 web-view的形式 需要对方在web-view顶部加点东西 对方打的app的web-view始终是全屏的状态&#xff0c;对方表示做不到我要的效果 emmmmmm。。。。。。 于是乎 自己搭了个demo 本地h5跑起来审查了下代码&#xff0c;发现web-view是给绝对定…

关于 CAM350打开钻孔文件时提示出错处理

自动导入CAM350出错时&#xff0c;需要单独导入通孔文件或者槽孔文件查看 第一步&#xff1a; 第二步&#xff1a; 下图中的几处值要与出Allegro中导出文件时的设置一致。 这样应该就能正常查看钻孔文件。

WPF中的Window类

控件分类 在第一篇文章.Net Core和WPF介绍中的WPF的功能和特性部分根据功能性介绍了WPF的控件 名称。 在接下来的文章中&#xff0c;将会详细的介绍各个控件的概念及使用。 主要包括&#xff1a; 内容控件&#xff1a;Label、Button、CheckBox、ToggleButton、RadioButton、…

【长文梳理Webserver核心】框架篇

感谢前人的总结&#xff0c;让一个小白快速成长&#xff0c;那我也贡献一份自己的力量~ 大框架梳理从main函数开始学习 大框架梳理 先摆图&#xff1a; 目光先放到最上面的两个小框架&#xff0c;半同步/半反应堆线程池和异步日志系统&#xff0c;日志系统晓得伐&#xff1f;…

jvm垃圾收集器简介

串行垃圾收集器 串行垃圾收集器&#xff0c;是指使用单线程进行垃圾回收&#xff0c;垃圾回收时&#xff0c;只有一个线程在工作&#xff0c;Java应用中的所有线程都要暂停&#xff0c;等待垃圾回收的完成。这种现象称之为STW(Stop-The-World)&#xff0c;一般的javaweb应用中…

深入理解 pnpm(Performant NPM) 的实现原理及其与 npm 的区别

深入理解 pnpm 的实现原理及其与 npm 的区别 在 JavaScript 生态系统中&#xff0c;包管理器是开发者日常工作中不可或缺的工具。npm&#xff08;Node Package Manager&#xff09;作为 Node.js 的默认包管理器&#xff0c;已经广泛应用于各种项目中。然而&#xff0c;随着项目…

略谈发展测量方法论-高敏雪老师的文章解读与收获

历史地看&#xff0c; GDP 被誉为“世纪性发明”&#xff0c;我们曾经将其视为衡量一国经济发展的重要工具&#xff1b;现实地看&#xff0c;“超越 GDP”是当前出现的带有国际性的口号&#xff0c;内在地包含着对 GDP作为发展指标的批评和替代。 如何看到发展&#xff1f; 如…

apisix云原生网关

定义 企业级网关通过域名、路由将请求分发到对应的应用上&#xff0c;通常承载数千个服务的流量&#xff0c;对稳定性有较高要求。 CNCF全景图 选型 Kubernetes抽象出两个核心概念&#xff1a;Service&#xff0c;为多个Pod提供统一的访问入口&#xff1b;Ingress&#xff…

【STM32-HAL库】实现微秒、毫秒、纳秒延时。(STM32F4系列)(附带工程下载链接)

使用了本代码后不能使用HAL库自带的HAL_Delay函数 使用了本代码后不能使用HAL库自带的HAL_Delay函数 使用了本代码后不能使用HAL库自带的HAL_Delay函数 一、新建工程 可以参考我的新建工程系列教程 stm32-HAL库cubeMX新建工程教程&#xff08;以F103C8T6为例&#xff09;ht…

【长文梳理webserver核心】核心类篇

前言 有三个核心组件支撑一个reactor实现 [持续] 的 [监听] 一组fd&#xff0c;并根据每个fd上发生的事件 [调用] 相应的处理函数。这三个组件就是 EventLoop 、Channel 以及 Poller 三个类&#xff0c;其中 EventLoop 可以看作是对业务线程的封装&#xff0c;而 Channel 可以看…

从零开始搭建一个node.js后端服务项目

一、下载node.js及配置环境 网上很多安装教程&#xff0c;此处就不再赘述了 版本信息 C:\Users\XXX>node -v v20.15.0C:\Users\XXX>npm -v 10.7.0 二、搭建node.js项目及安装express框架 在任意位置创建一个项目文件夹&#xff0c;此处项目文件夹名为test&#xff0…