【quartz】从数据库中读取配置实现动态定时任务

前言

        在Java中定时任务是很常见的,使用@Scheduled注解即可实现定时,但是在生产环境中可能会遇到页面上自主配置定时任务的定时时间,这种情况下单独用@Scheduled注解很难实现定时任务,所以我们可以将定时任务存储在数据库中并通过quartz实现动态定时任务。


配置依赖

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>

实现方式

1、定时任务表实体

@Data
@TableName("SCHEDULE_JOB")
public class ScheduleJob {

    /**
     * 序号
     */
    @TableId(value = "ID", type = IdType.ID_WORKER_STR)
    private String id;

    /**
     * 任务名称
     */
    @TableField("JOB_NAME")
    private String jobName;

    /**
     * 任务分组
     */
    @TableField("JOB_GROUP")
    private String jobGroup;

    /**
     * 任务状态 是否启动任务,2:失效;1:有效
     */
    @TableField("JOB_STATUS")
    private Integer jobStatus;

    /**
     * cron表达式,推荐使用6域的
     */
    @TableField("CRON_EXPRESSION")
    private String cronExpression;

    /**
     * 描述
     */
    @TableField("DESCRIPTION")
    private String description;

    /**
     * 任务执行时调用哪个类的方法 包名+类名,全路径
     */
    @TableField("BEAN_CLASS")
    private String beanClass;

    /**
     * 任务是否有状态
     */
    @TableField("IS_CONCURRENT")
    private Integer isConcurrent;

    /**
     * spring bean 对应定时任务的类名,首字母小写
     */
    @TableField("SPRING_ID")
    private String springId;

    /**
     * 任务调用的方法名
     */
    @TableField("method_name")
    private String methodName;

    @TableField("CREATE_TIME")
    private Date createTime;

    @TableField("UPDATE_TIME")
    private Date updateTime;
}

2、反射调用scheduleJob中定义的方法

import com.saas.reptile.common.result.Result;
import com.saas.reptile.entity.po.ScheduleJob;
import com.saas.reptile.utils.LogUtils;
import com.saas.reptile.utils.SpringUtils;
import com.saas.reptile.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;

public class TaskUtils {

    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 通过反射调用scheduleJob中定义的方法
     *
     * @param scheduleJob
     */
    public static void invokMethod(ScheduleJob scheduleJob) {

        try {//添加最大的异常捕获
            String springId = scheduleJob.getSpringId();
            Object object = null;
            Class clazz = null;

            //根据反射来进行
            if (StringUtils.isNotBlank(springId)) {
                object = SpringUtils.getBean(springId);
            }

            if (object == null && StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
                String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "]-在spring 中没有这个 springId, 通过 class type 获取中...";
                LogUtils.info(jobStr, scheduleJob.getBeanClass());
                try {
                    clazz = Class.forName(scheduleJob.getBeanClass());
                    object = SpringUtils.getBean(clazz);
                    if(object == null){
                        jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "]-在spring 中没有获得 bean, 调用 spring 方法再次构建中...";
                        LogUtils.info(jobStr, scheduleJob.getBeanClass());
                        object = SpringUtils.getBeanByType(clazz);
                    }
                    if (StringUtils.isNotBlank(springId)) {
                        SpringUtils.setBean(springId, object);
                        LogUtils.info("spring bean 构建完成并加入到容器中 ", scheduleJob.getBeanClass());
                    }
                    LogUtils.info("定时任务 spring bean 构建成功! ", scheduleJob.getBeanClass());
                } catch (Exception e) {
                    LogUtils.error("定时任务 spring bean 构建失败了!!! ", scheduleJob.getBeanClass(), e);
                    Result.fail(e);
                    return;
                }
            }

            clazz = object.getClass();
            Method method = null;
            try {
                method = clazz.getDeclaredMethod(scheduleJob.getMethodName());
            } catch (NoSuchMethodException e) {
                String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "] = 未启动成功,方法名设置错误!!!";
                LogUtils.error(jobStr, e);
            } catch (SecurityException e) {
                LogUtils.error("TaskUtils发生异常", e);
                Result.fail(e);
            }
            if (method != null) {
                try {
                    method.invoke(object);
                    LogUtils.info("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动成功");
                } catch (Exception e) {
                    Result.fail(e);
                    LogUtils.error("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!", e);
                    return;
                }
            } else {
                String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!";
                LogUtils.error(jobStr, clazz.getName(), "not find method ");
            }

        } catch (Exception e) {//添加最大的异常捕获
            Result.fail(e);
            LogUtils.error("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!", e);
        }

    }
}

3、创建正常状态下的job执行工厂

import com.saas.reptile.entity.po.ScheduleJob;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class QuartzJobFactory implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
        TaskUtils.invokMethod(scheduleJob);
    }
}

4、若一个方法一次执行不完下次轮转时则等待改方法执行完后才执行下一次操作

import com.saas.reptile.entity.po.ScheduleJob;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

@DisallowConcurrentExecution
public class QuartzJobFactoryDisallowConcurrentExecution implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
        TaskUtils.invokMethod(scheduleJob);
    }
}

5、创建需要运行的定时任务类

@Component
public class ClassAtmosphereTask {

    public void work(){
        // do something
    }

}

6、SpringBean工厂工具类

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils implements BeanFactoryPostProcessor {

    // Spring应用上下文环境
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取对象
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBeanByType(Class<T> clzee) throws BeansException {
        try {
            return beanFactory.createBean(clzee);
        } catch (NoSuchBeanDefinitionException e) {
            return null;
        }

    }

    /**
     * 注入一个对象
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    @SuppressWarnings("unchecked")
    public static void setBean(String springId, Object obj) throws BeansException {
        beanFactory.registerSingleton(springId, obj);
    }

    /**
     * 获取类型为requiredType的对象
     * @return
     * @throws BeansException
     */
    public static <T> T getBean(Class<T> clz) throws BeansException {
        try {
            @SuppressWarnings("unchecked")
            T result = (T) beanFactory.getBean(clz);
            return result;
        } catch (NoSuchBeanDefinitionException e) {
            return null;
        }
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
     * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getAliases(name);
    }
}

7、其他用到的工具类

  • 字符串工具类
public class StringUtils {
    public final static String REG_DIGIT = "[0-9]*";
    public final static String REG_CHAR = "[a-zA-Z]*";
    public final static String EMPTY = "";

    /**
     * 判断是否为空
     */
    public static boolean isEmpty(Object... obj) {
        if(obj == null)
            return true;
        for(Object object : obj) {
            if (object == null)
                return true;
            if (object.toString().trim().length() == 0)
                return true;
        }

        return false;
    }

    public static boolean isBlankEmpty(Object obj) {
        if (obj == null || "".equals(obj) || "".equals(obj.toString().trim()) || "null".equalsIgnoreCase(obj.toString()))
            return true;

        return false;
    }

    /**
     * 是否空,或者为空串,或者为"null"
     * @author guoyu
     */
    public static boolean isBlankEmpty(Object... objs) {
        if (objs == null || objs.length == 0)
            return true;
        for (Object obj : objs) {
            if (isBlankEmpty(obj)) {
                return true;
            }
        }

        return false;
    }

    public static boolean isNotBlank(String pattern) {
        return !isBlankEmpty(pattern);
    }

    public static boolean isBlank(String pattern) {
        return isBlankEmpty(pattern);
    }

    public static String formatCountNames(String nameList) {
        String[] names = nameList.split(",");

        Map<String, Integer> nameCount = new HashMap<String, Integer>();
        for(String name : names) {
            if(StringUtils.isEmpty(name)) continue;
            if(nameCount.containsKey(name)) {
                Integer count = nameCount.get(name) + 1;
                nameCount.put(name, count);
            } else {
                nameCount.put(name, 1);
            }
        }

        StringBuilder newNames = new StringBuilder();
        for(String key : nameCount.keySet()) {
            if(StringUtils.isEmpty(key)) continue;
            Integer count = nameCount.get(key);
            String splitChar = newNames.length() > 0 ? "," : "";
            newNames.append(splitChar).append(key).append("x").append(count);
        }

        return newNames.toString();
    }


    public static boolean isDigit(String str){
        return isNumeric(str);
    }

    public static boolean isChar(String str){
        return str.matches(REG_CHAR);
    }

    public static Boolean isNotEmpty(Object... obj) {
        Boolean r = StringUtils.isEmpty(obj);
        return !r;
    }

    public static boolean isNumeric(String str) {
        if(isBlankEmpty(str)) return false;
        for (int i = str.length(); --i >= 0;) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * string 中的 str 在倒数 num 中的位置
     * @author guoyu
     */
    public static int stringLastlndex(String string, String str, int num) {
        int indexOf = string.lastIndexOf(str);
        if(num > 1){
            return stringLastlndex(string.substring(0, indexOf), str, num - 1);
        } else {
            return indexOf;
        }
    }


    public static String getValue(Object val) {
        return val == null ? "" : val.toString().replace("\n", "");
    }

    public static String getFileName(boolean type, Date startDate, String tableName){
        String dateString = dateFormat(startDate,"yyyyMMdd");
        StringBuffer stringBuffer = new StringBuffer(dateString);
        stringBuffer.append("_");
        stringBuffer.append(tableName);
        stringBuffer.append("_");
        if (type) {
            stringBuffer.append("insert&");
        } else {
            stringBuffer.append("update&");
        }
        return stringBuffer.toString();
    }

    public static String getRefundNumber(Integer payRefundNum) {
        if (payRefundNum == null) payRefundNum = 0;
        payRefundNum = payRefundNum + 1;
        String string = String.valueOf(payRefundNum);
        if (string.length() == 1) {
            return "0"+string;
        }
        return string;

    }

    private static String dateFormat(Date date, String datePattern) {
        if(date == null) return "";
        if(datePattern == null) datePattern = "yyyy-MM-dd";
        SimpleDateFormat df = new SimpleDateFormat(datePattern, Locale.UK);
        return df.format(date);
    }
}
  • 日志工具类
import org.apache.commons.logging.LogFactory;


public class LogUtils {
    private static final org.apache.commons.logging.Log logger;
    private static final Object lock = new Object();

    static {
        synchronized (lock) {
            logger = LogFactory.getLog(LogUtils.class);
        }
    }

    public static void info(Object... msgs) {
        StringBuilder stringBuilder = new StringBuilder();
        Throwable e = null;
        for (Object msg : msgs) {
            if (msg != null) {
                if (msg instanceof Throwable) {
                    e = (Throwable) msg;
                } else {
                    stringBuilder.append(msg).append(" ");
                }
            }
        }
        logger.info(stringBuilder, e);
    }

    public static void error(Object... msgs) {
        StringBuilder stringBuilder = new StringBuilder();
        Throwable e = null;
        for (Object msg : msgs) {
            if (msg != null) {
                if (msg instanceof Throwable) {
                    e = (Throwable) msg;
                } else {
                    stringBuilder.append(msg).append(" ");
                }
            }
        }
        logger.error(stringBuilder, e);
    }

    public static void warn(Object... msgs) {
        StringBuilder stringBuilder = new StringBuilder();
        Throwable e = null;
        for (Object msg : msgs) {
            if (msg != null) {
                if (msg instanceof Throwable) {
                    e = (Throwable) msg;
                } else {
                    stringBuilder.append(msg).append(" ");
                }
            }
        }
        logger.warn(stringBuilder, e);
    }

    public static void debug(Object... msgs) {
        StringBuilder stringBuilder = new StringBuilder();
        Throwable e = null;
        for (Object msg : msgs) {
            if (msg != null) {
                if (msg instanceof Throwable) {
                    e = (Throwable) msg;
                } else {
                    stringBuilder.append(msg).append(" ");
                }
            }
        }
        logger.debug(stringBuilder, e);
    }
}
  • 数据分析常量类
public final class Constant {

    private Constant() {
    }

    // 定时任务运行状态:运行
    public static final Integer STATUS_RUNNING = 1;

    // 定时任务运行状态:停止
    public static final Integer STATUS_NOT_RUNNING = 2;

    // 定时任务是否有状态:有(默认为1,弃用)
    public static final Integer CONCURRENT_IS = 1;

    // 定时任务是否有状态:无(默认为1,弃用)
    public static final Integer CONCURRENT_NOT = 0;
}
  • 接口返回参数类
@Data
public class Result<T> {

    private Integer code;

    private T data;

    private String msg;

    public Result() {
        super();
    }

    public Result(T data) {
        this.data = data;
    }

    public static Result success()
    {
        return success("");
    }

    public static <T> Result success(T data) {
        Result<T> result = new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName());
        result.setData(data);
        return result;
    }

    public static <T> Result<T> fail(ResultEnum resultEnum) {
        return createResult(null, resultEnum.getCode(), resultEnum.getName());
    }

    public static <T> Result<T> fail(Integer code, String message) {
        return createResult(null, code, message);
    }

    public static <T> Result<T> fail(Throwable e) {
        //打印异常栈信息到控制台
        e.printStackTrace();
        return createResult(null, 500, "服务器发生错误");
    }

    private static <T> Result<T> createResult(T data, Integer code, String message) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setData(data);
        r.setMsg(message);
        return r;
    }

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

其中ResultEnum类如下:

public enum ResultEnum {

    SUCCESS(200,"请求成功"),
    UN_KNOWN_ERROR(-1, "未知错误"),

    PARAM_NULL_ERROR(10001,"参数为空"),

    PERMISSION_ACCESS_DENIED(50001, "permission access denied");


    private int code;
    private String name;

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

    public int getCode() {
        return this.code;
    }

    public String getName() {
        return this.name;
    }

    public static ResultEnum getNameByCode(int code) {
        for (ResultEnum resultEnum : ResultEnum.values()) {
            if (code == resultEnum.getCode()) {
                return resultEnum;
            }
        }
        return null;
    }
}

8、sql语句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for schedule_job
-- ----------------------------
DROP TABLE IF EXISTS `schedule_job`;
CREATE TABLE `schedule_job`  (
  `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键',
  `job_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务名称',
  `job_group` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务分组',
  `job_status` int(0) DEFAULT NULL COMMENT '任务状态 是否启动任务;1:有效;2:失效',
  `cron_expression` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表达式,推荐使用6域的',
  `description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '定时任务描述',
  `bean_class` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务执行时调用哪个类的方法 包名+类名,全路径',
  `is_concurrent` int(0) DEFAULT 1 COMMENT '任务是否有状态;0:没有;1:有',
  `spring_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '注册的Bean,所以与类名保持一致,首字母小写',
  `method_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务调用的方法名',
  `create_time` datetime(0) DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of schedule_job
-- ----------------------------
INSERT INTO `schedule_job` VALUES ('1', 'ClassAtmosphereTask', 'ClassAtmosphereTask', 1, '0 30 23 * * ?', '班级氛围值定时任务', 'com.saas.reptile.task.ClassAtmosphereTask', 1, 'classAtmosphereTask', 'work', '2022-06-25 21:38:42', '2022-06-25 21:38:45');

SET FOREIGN_KEY_CHECKS = 1;

注释的很详细,其中:

job_name和job_group保持一致即可;

is_concurrent默认为1;

任务执行的类要全路径等等。

9、方法实现

写在service的实现类里

package com.saas.reptile.service.impl;

import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.saas.reptile.common.quartz.QuartzJobFactory;
import com.saas.reptile.common.quartz.QuartzJobFactoryDisallowConcurrentExecution;
import com.saas.reptile.entity.po.ScheduleJob;
import com.saas.reptile.mapper.ScheduleJobMapper;
import com.saas.reptile.service.ScheduleJobService;
import com.saas.reptile.utils.Constant;
import com.saas.reptile.utils.LogUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;


@Service
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob>
        implements ScheduleJobService {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    /**
     * 添加定时任务到数据库
     * @param scheduleJob
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTask(ScheduleJob scheduleJob) {
        this.baseMapper.insert(scheduleJob);
    }

    /**
     * 改变定时任务状态
     * @param id
     * @param status
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void changeJobStatus(String id, Integer status) throws SchedulerException {
//        EntityWrapper<ScheduleJob> wrapper = new EntityWrapper<>();
//        wrapper.eq("id",id);
//        String setSql = "job_status = " + status;
//        this.baseMapper.updateForSet(setSql, wrapper);
        ScheduleJob job = selectById(id);
        if (status == 1){
            // 启动
            job.setJobStatus(status);
            addJob(job);
        } else if (status == 2) {
            // 停止
            deleteJob(job);
            job.setJobStatus(status);
        }
        // 更新定时任务
        updateById(job);
    }

    /**
     * 更新定时任务cron表达式与运行状态
     * @param jobId     定时任务主键ID
     * @param cron      表达式
     * @param status    定时状态
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateCronStatus(String jobId, String cron, Integer status) throws SchedulerException {
        ScheduleJob job = selectById(jobId);
        if (status == 1){
            // 启动
            job.setJobStatus(status);
            job.setCronExpression(cron);
            addJob(job);
        } else if (status == 2) {
            // 停止
            deleteJob(job);
            job.setJobStatus(status);
            job.setCronExpression(cron);
        }
        // 更新定时任务
        updateById(job);
    }

    /**
     * 添加任务
     *
     * @param job
     * @throws SchedulerException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void addJob(ScheduleJob job) throws SchedulerException {
        // 定时任务运行状态为1则进入下一步
        if (job == null || !Constant.STATUS_RUNNING.equals(job.getJobStatus())) {
            return;
        }
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        LogUtils.info(scheduler + ".......................................................................................add");
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        // 不存在,创建一个
        if (null == trigger) {
            Class clazz = Constant.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;

            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();

            jobDetail.getJobDataMap().put("scheduleJob", job);

            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

            trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // Trigger已存在,那么更新相应的定时设置
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    @PostConstruct
    public void init() throws Exception {
        LogUtils.info("实例化List<ScheduleJob>,从数据库读取....",this);
        // 这里获取任务信息数据
        List<ScheduleJob> jobList = selectList(null);
        for (ScheduleJob job : jobList) {
            addJob(job);
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     * @throws SchedulerException
     */
    public List<ScheduleJob> getAllJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                ScheduleJob job = new ScheduleJob();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                job.setDescription("触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(Integer.parseInt(triggerState.name()));
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public List<ScheduleJob> getRunningJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            ScheduleJob job = new ScheduleJob();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("触发器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(Integer.parseInt(triggerState.name()));
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCronExpression(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }

    /**
     * 暂停一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.deleteJob(jobKey);

    }

    /**
     * 立即执行job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新job时间表达式
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();

        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());

        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());

        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

        scheduler.rescheduleJob(triggerKey, trigger);
    }
}

1、项目启动后首先运行上述方法里的init()方法,查询数据库表里的所有定时任务;

2、然后到添加job方法【addJob】,将状态为1的定时任务推到Scheduler计划中

3、还提供了获取所有计划中的任务列表、查询正在运行的job、暂停一个job等方法,不过暂时我没有用到。

4、本项目是通过mybatis-plus进行数据库操作,当更新运行状态时,如果状态为1则启动job,如果状态为2则停止job。

5、控制层方法省略,按实际生产环境来。

参考资料:https://gitee.com/gangye/springboot_quartz_schedule

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值