Springboot整合Quartz实现定时任务数据库动态配置

引入maven相关依赖

        
            org.quartz-scheduler
            quartz-jobs
            2.3.2
        

        
            org.quartz-scheduler
            quartz
            2.3.2
        

        
            org.springframework.boot
            spring-boot-starter-quartz
        

根据quartz官网下载源码,找到mysql innoDB的建表脚本

创建一个新的数据库,执行建表SQL

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(200) NOT NULL,
JOB_GROUP VARCHAR(200) 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(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) 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(200) 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(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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(200) 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(200) 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(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) 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(200) NULL,
JOB_GROUP VARCHAR(200) 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(200) 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;

根据自身的项目建立一个自定义任务列表,用于前端展示以及创建新的任务、控制暂停、恢复定时任务等操作

DROP TABLE IF EXISTS `qrtz_task`;
CREATE TABLE `qrtz_task`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `job_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '任务名称',
  `job_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '任务所属组名称',
  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'cron表达式',
  `job_class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '任务执行时调用哪个类的方法(包名.类名:com.springboot.task.HelloJob)',
  `trigger_state` tinyint(1) NULL DEFAULT NULL COMMENT '任务状态(0:禁用/1:启用)',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '任务描述',
  `create_user` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改人',
  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  `is_deleted` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除(0:正常/1:已删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '自定义定时任务列表' ROW_FORMAT = Dynamic;
在项目resources目录下自定义一个quartz.properties配置文件
#quartz集群配置
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceid=AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount=50
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority=5
#============================================================================
# Configure JobStore
#============================================================================
# 信息保存时间 默认值60秒
org.quartz.jobStore.misfireThreshold=60000
#数据保存方式为数据库持久化
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#JobDataMaps是否都为String类型
org.quartz.jobStore.useProperties=false
#数据库别名 随便取
org.quartz.jobStore.dataSource=myDS
#表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix=QRTZ_
#是否加入集群
org.quartz.jobStore.isClustered=true
#调度实例失效的检查时间间隔
org.quartz.jobStore.clusterCheckinInterval=20000
项目当中的yml配置,使用的是spring-cloud-alibaba-nacos注册中心加dubbo
server:
  port: 8085
spring:
  application:
    name: dubbo-task
  cloud:
    nacos:
      discovery:
        # nacos集群方式就是把所有数据持久化到mysql数据库
        # 多个注册中心配置ip1:port1,ip2:port2,ip3:port3
        server-addr: 192.168.3.192:8848
  main:
    allow-bean-definition-overriding: true
  datasource:
    # Mysql配置
    # com.mysql.jdbc.Driver是mysql-connector-java 5版本的驱动名称,5版本之后替换为com.mysql.cj.jdbc.Driver
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/dubbo_task?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT&tinyInt1isBit=false
    username: root
    password: 123456
    #schema-username: root
    #schema-password: 123456
    #schema: classpath:quartz_tables.sql
    #initialization-mode: always
# 如果没有调用其他微服务的操作,无需dubbo相关配置
dubbo:
  protocol:
    name: dubbo
    port: -1
  registry:
    address: spring-cloud://192.168.3.192
  cloud:
    # 订阅服务提供者, 要订阅多个服务,请使用,(英文逗号)作为分隔符
    subscribed-services: dubbo-provider
    # 解决Dubbo中生产者未启动, 使用@Reference实例化的对象都是null
    # 消费者启动报错的问题, 很多时候服务是提供者也是消费者
    # 启动时检查提供者是否存在,true报错,false忽略  默认值true
    check: false
    # 远程服务调用超时时间(毫秒), 默认值1000
    timeout: 20000

mybatis-plus:
  # MyBatis 配置文件位置,如果有单独的 MyBatis 配置,请将其路径配置到 configLocation 中
  configLocation:
  # MyBatis Mapper 所对应的 XML 文件位置
  mapperLocations: classpath:/mapper/**/*.xml
  configuration:
    # 是否开启自动驼峰命名规则(camel case)映射
    # 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
    # 如果数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
    mapUnderscoreToCamelCase: true
    # 是否开启Mybatis二级缓存,默认为 true
    cacheEnabled: false
  global-config:
    db-config:
      # 表名是否使用驼峰转下划线命名,只对表名生效,默认 true
      tableUnderline: true
      logicDeleteField: isDeleted  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logicDeleteValue: 1 # 逻辑已删除值(默认为 1)
      logicNotDeleteValue: 0 # 逻辑未删除值(默认为 0)
创建一个JobFactory类,用于将JobFactory注入到spring里
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

/**
 * @author Administrator
 * @description Job的创建是由Quartz通过反射创建的
 * 并未交由Spring容器创建。故原则上来说,是无法在Job实例中使用依赖注入
 * @date 2021-03-12 16:29
 */
@Component
public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        // 使用AutowireCapableBeanFactory完成对IOC外Bean的注入操作
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }



}

创建一个QuartzConfig类,用于注入Scheduler相关的Bean

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

/**
 * @author Administrator
 * @description
 * @date 2021-03-12 16:27
 */
@Configuration
public class QuartzConfig {

    @Autowired
    private JobFactory jobFactory;
    @Autowired
    private DataSource dataSource;

    @Bean(name = "schedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setQuartzProperties(quartzProperties());
        schedulerFactoryBean.setJobFactory(jobFactory);
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        // 设置自行启动
        schedulerFactoryBean.setAutoStartup(true);
        // 延时启动
        schedulerFactoryBean.setStartupDelay(1);
        // 使用yml文件配置中的dataSource
        schedulerFactoryBean.setDataSource(dataSource);
        return schedulerFactoryBean;
    }

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

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

}

定义一个恢复任务、暂停任务使用的枚举类,该类可有可无,看自身需求,主要用于前端页面操作暂停、启用定时任务时,后台判断状态字段的值

/**
 * @author Administrator
 * @description
 * @date 2021-03-12 18:02
 */
public enum TaskStatusEnum {

    NORMAL(1, "正常"),
    PAUSED(0, "暂停");
    private int code;
    private String name;

    TaskStatusEnum(int code, String name) {
        this.code = code;
        this.name = name;
    }

    public int getCode() {
        return code;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

创建一个包含新增、暂停、恢复、删除、修改cron表达式、用于操作定时任务的类

import com.springcloud.dubbo_task.dto.TaskDTO;
import com.springcloud.dubbo_task.common.TaskStatusEnum;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author Administrator
 * @description
 * @date 2021-03-16 14:36
 */
@Component
public class QuartzJobManager {

    private static final Logger logger = LoggerFactory.getLogger(QuartzJobManager.class);

    @Autowired
    private Scheduler scheduler;

    /**
     * 根据参数配置创建新的定时任务
     * @param task
     */
    @SuppressWarnings("unchecked")
    public void addJob(TaskDTO task) {
        try {
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            Class jobClass = (Class) (Class.forName(task.getBeanClass()).newInstance()
                    .getClass());
            addJob(task.getJobName(), task.getJobGroup(), task.getCronExpression(),jobClass);
            // 是否启用任务, 默认启用
            if (task.getJobStatus() == TaskStatusEnum.NORMAL.getCode()) {
                pauseJob(task.getJobName(), task.getJobGroup());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建一个新的定时任务
     * @param jobName 任务名称
     * @param jobGroup 任务所属组名
     * @param cronExpression cron表达式
     * @param clazz 任务类
     */
    public void addJob(String jobName, String jobGroup, String cronExpression, Class clazz) {
        addJob(jobName, jobGroup, cronExpression, clazz, null);

    }

    /**
     * 创建一个新的定时任务,可传参
     * @param jobName
     * @param jobGroup
     * @param cronExpression
     * @param clazz
     * @param map
     */
    public void addJob(String jobName, String jobGroup, String cronExpression, Class clazz,
                       Map map) {
        try {
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(jobName, jobGroup).build();
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
            if (map != null) {
                trigger.getJobDataMap().putAll(map);
            }
            scheduler.scheduleJob(jobDetail, trigger);
            if (scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新定时任务的频率
     * @param jobName
     * @param jobGroup
     * @param cronExpression
     */
    public void updateJob(String jobName, String jobGroup, String cronExpression) {
        updateJob(jobName, jobGroup, cronExpression, null);
    }

    /**
     * 更新频率和参数
     * @param jobName
     * @param jobGroup
     * @param cronExpression
     * @param map
     */
    public void updateJob(String jobName, String jobGroup, String cronExpression, Map map) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //修改map
            if (map != null) {
                trigger.getJobDataMap().putAll(map);
            }
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 只更新参数
     * @param jobName
     * @param jobGroup
     * @param map
     */
    public void updateJob(String jobName, String jobGroup, Map map) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //修改map
            trigger.getJobDataMap().putAll(map);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 暂停指定任务
     * @param jobName
     * @param jobGroup
     */
    public void pauseJob(String jobName, String jobGroup) {
        try {
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 恢复任务
     * @param jobName
     * @param jobGroup
     */
    public void resumeJob(String jobName, String jobGroup) {
        try {
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除任务
     * @param jobName
     * @param jobGroup
     */
    public void deleteJob(String jobName, String jobGroup) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 启动所有任务
     */
    public void startJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有定时任务
     */
    public void shutdownJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取所有定时任务列表
     * @return
     */
    public List> getAllJob() {
        GroupMatcher matcher = GroupMatcher.anyJobGroup();
        List> jobList = new ArrayList<>();
        Set jobKeys = null;
        try {
            jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map job = new HashMap<>();
                    job.put("jobName", jobKey.getName());
                    job.put("jobGroup", jobKey.getGroup());
                    job.put("trigger", trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.put("triggerState", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.put("cronExpression", cronExpression);
                    }
                    jobList.add(job);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }
}

根据自身需求创建一个用于前端调用后台接口传参的TaskDTO,对应自定义的任务列表字段

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * @author Administrator
 * @description
 * @date 2021-03-16 16:11
 */
@Data
public class TaskDTO implements Serializable {

    private static final long serialVersionUID = -2804443468276676760L;

    /**
     * 主键
     */
    private int id;
    /**
     * 任务名
     */
    private String jobName;
    /**
     * 任务组名
     */
    private String jobGroup;
    /**
     * cron表达式
     */
    private String cronExpression;
    /**
     * 格式:包名.类名
     * com.springcloud.dubbo_task.task.HelloJob
     */
    private String jobClassName;
    /**
     * 任务状态(0:禁用/1:启用)
     */
    private int triggerState= 1;
    /**
     * 任务描述
     */
    private String description;
}

创建一个用来执行指定的定时任务的类

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * @author Administrator
 * @description @DisallowConcurrentExecution : 该注解用在实现Job的类上面,意思是不允许并发执行
 * 注org.quartz.threadPool.threadCount的数量有多个的情况,@DisallowConcurrentExecution才生效
 * @date 2021-03-15 18:04
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class HelloJob implements Job {

    private static final Logger logger = LoggerFactory.getLogger(HelloJob.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        logger.info("----------HelloJob定时任务执行逻辑----------");
        System.out.println("==========定时任务执行==========" + new Date());
        // 可以调用其他微服务的接口进行业务逻辑处理
    }
}

前端调用接口,动态操作定时任务

import com.springcloud.dubbo_api.service.HelloServiceApi;
import com.springcloud.dubbo_task.dto.TaskDTO;
import com.springcloud.dubbo_task.service.TaskService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author Administrator
 * @description
 * @date 2021-03-16 18:03
 */
@RestController
@RequestMapping(value = "/job")
public class TaskController {

    @Autowired
    private TaskService taskService;
    @Reference
    private HelloServiceApi helloServiceApi;

    @GetMapping(value = "hello")
    public void hello() {
        String name = helloServiceApi.hello("task");
        System.out.println("====调用服务返回值====" + name);
    }

    /**
     * 添加一个新的任务
     * @param task
     */
    @PostMapping(value = "add")
    public void add(@RequestBody TaskDTO task) {
        taskService.add(task);
    }

    /**
     * 变更任务的执行频率
     * @param task
     */
    @PutMapping(value = "updateCron")
    public void updateCron(@RequestBody TaskDTO task) {
        taskService.updateCron(task);
    }

    /**
     * 暂停/恢复任务
     * @param task
     */
    @PutMapping(value = "updateStatus")
    public void updateStatus(@RequestBody TaskDTO task) {
        taskService.updateStatus(task);
    }

    /**
     * 删除任务
     * @param task
     */
    @DeleteMapping(value = "deleteJob")
    public void deleteJob(@RequestBody TaskDTO task) {
        taskService.deleteJob(task);
    }
}

TaskService

import com.springcloud.dubbo_task.dto.TaskDTO;

/**
 * @author Administrator
 * @description
 * @date 2021-03-16 17:25
 */
public interface TaskService {

    void add(TaskDTO task);

    void updateCron(TaskDTO task);

    void updateStatus(TaskDTO task);

    void deleteJob(TaskDTO task);

    void initSchedule();
}

业务逻辑实现类TaskServiceImpl

import com.springcloud.dubbo_task.dto.TaskDTO;
import com.springcloud.dubbo_task.common.TaskStatusEnum;
import com.springcloud.dubbo_task.entity.QrtzTaskEntity;
import com.springcloud.dubbo_task.manage.QuartzJobManager;
import com.springcloud.dubbo_task.mapper.TaskMapper;
import com.springcloud.dubbo_task.service.TaskService;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.List;

/**
 * @author Administrator
 * @description
 * @date 2021-03-16 17:26
 */
@Service
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private QuartzJobManager quartzJobManager;

    @Override
    public void add(TaskDTO task) {
        try {
            QrtzTaskEntity taskEntity = new QrtzTaskEntity();
            BeanUtils.copyProperties(taskEntity, task);
            taskEntity.setCreateUser("张三");
            taskEntity.setCreateTime(new Date());
            taskEntity.setIsDeleted(0);
            int result = taskMapper.insertJob(taskEntity);
            if (result > 0) {
                quartzJobManager.addJob(task);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void updateCron(TaskDTO task) {
        QrtzTaskEntity taskEntity = new QrtzTaskEntity();
        try {
            BeanUtils.copyProperties(taskEntity, task);
            taskEntity.setUpdateUser("李四");
            taskEntity.setUpdateTime(new Date());
            taskMapper.updateCron(taskEntity);
            quartzJobManager.updateJob(task.getJobName(), task.getJobGroupName(), task.getCronExpression());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void updateStatus(TaskDTO task) {
        QrtzTaskEntity taskEntity = new QrtzTaskEntity();
        try {
            BeanUtils.copyProperties(taskEntity, task);
            taskEntity.setUpdateUser("李四");
            taskEntity.setUpdateTime(new Date());
            taskMapper.updateStatus(taskEntity);
            if (task.getJobStatus() == TaskStatusEnum.NORMAL.getCode()) {
                quartzJobManager.resumeJob(task.getJobName(), task.getJobGroup());
            } else {
                quartzJobManager.pauseJob(task.getJobName(), task.getJobGroup());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteJob(TaskDTO task) {
        QrtzTaskEntity taskEntity = new QrtzTaskEntity();
        try {
            BeanUtils.copyProperties(taskEntity, task);
            taskEntity.setUpdateUser("李四");
            taskEntity.setUpdateTime(new Date());
            taskEntity.setIsDeleted(1);
            taskMapper.deleteJob(taskEntity);
            quartzJobManager.deleteJob(task.getJobName(), task.getJobGroup());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void initSchedule() {
        List list = taskMapper.listTasks();
        for(TaskDTO task : list) {
            if (task.getJobStatus() == TaskStatusEnum.NORMAL.getCode()) {
                quartzJobManager.resumeJob(task.getJobName(), task.getJobGroup());
            }
        }
    }
}

TaskMapper.xml 





    

    
        insert into qrtz_task
          (
          job_name,job_group,cron_expression,job_class_name,trigger_state,description,
          create_user,create_time,is_deleted
          )
          values
          (
          #{jobName},#{jobGroupName},#{cronExpression},#{jobClassName},#{triggerState},#{description},
          #{createUser},#{createTime},#{isDeleted}
          )
    

    

    
        update qrtz_task
        
            
                job_name = #{jobName},
            
            
                job_group = #{jobGroup},
            
            
                cron_expression = #{cronExpression},
            
            
                job_class_name= #{jobClassName},
            
            
                trigger_state= #{triggerState},
            
            
                update_user = #{updateUser},
            
            
                update_time = #{updateTime},
            
        
        where id = #{id}
    

    
        update qrtz_task
        
            
                trigger_state= #{triggerState},
            
            
                update_user = #{updateUser},
            
            
                update_time = #{updateTime},
            
        
        where id = #{id}
    

    
        update qrtz_task
        
            
                is_deleted = #{isDeleted},
            
            
                update_user = #{updateUser},
            
            
                update_time = #{updateTime},
            
        
        where id = #{id}
    

整体pom.xml文件依赖配置



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.5.RELEASE
         
    
    com.springcloud
    dubbo_task
    0.0.1-SNAPSHOT
    dubbo_task
    dubbo_task project for Spring Boot

    
    
        UTF-8
        UTF-8
        1.8
        Hoxton.SR3
        2.2.1.RELEASE
        3.3.0
        2.9.2
    

    
        
        
        
            com.springcloud
            dubbo_api
            0.0.1-SNAPSHOT
        

        
            org.quartz-scheduler
            quartz-jobs
            2.3.2
        

        
            org.quartz-scheduler
            quartz
            2.3.2
        

        
            org.springframework.boot
            spring-boot-starter-quartz
        

        
            com.alibaba.cloud
            spring-cloud-starter-dubbo
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        

        
            org.projectlombok
            lombok
            true
        
        
        
            io.springfox
            springfox-swagger2
            ${swagger.version}
        
        
        
            io.springfox
            springfox-swagger-ui
            ${swagger.version}
        
        
        
            org.apache.commons
            commons-lang3
            3.9
        
        
        
            commons-beanutils
            commons-beanutils
            1.9.4
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


涉及到微服务之间调用的定时任务

package com.springcloud.dubbo_task.task;

import com.springcloud.dubbo_api.service.HelloServiceApi;
import org.apache.dubbo.config.annotation.Reference;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

/**
 * @author Administrator
 * @description 
 * @date 2021-03-17 14:11
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
//@Component  注意:无需加@Component注解,当前服务启动,而调用的微服务未启动时,会造成当前服务启动失败
public class ProviderTask implements Job {

    @Reference
    private HelloServiceApi helloServiceApi;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("==========调用另一个服务的接口执行业务处理==========" + new Date());
        try {
            helloServiceApi.hello("dubbo_task");
        } catch (Exception e) {
            JobExecutionException ex = new JobExecutionException(e);
            // true 表示立即重新执行该任务, 不断重试,直到成功,项目中不能确定其他服务中断的时间有多长,不建议设置该参数
            // 微服务中,比如当前为A服务,调用B服务的接口,而B服务中断重启,当B服务重启成功并且调用接口成功后退出该异常循环,类似心跳机制
            //ex.setRefireImmediately(true);
            // true 表示 Quartz 会自动取消所有与这个 job 有关的 trigger,从而避免再次运行 job
            // 当定时任务较多时,出现异常可记录该任务的信息到数据库,使用另一个定时任务不断扫描是否有异常的任务,进行恢复
            // 根据业务需求,如果定时任务执行间隔较长,建议手动恢复
            ex.setUnscheduleAllTriggers(true);
            throw ex;
        }
    }
}

spring-cloud-alibaba + dubbo + nacos注册中心 + quartz定时任务微服务之间调用的demo资源已上传

https://download.csdn.net/download/m0_37845840/15871428

你可能感兴趣的:(Spring,Cloud,Spring,boot,quartz)