SpringBoot 集成 quartz 动态配置定时任务

1.添加maven依赖

2.添加自定义的任务类,注意要实现job接口

package com.seagetech.seismic.manager.timer.job;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.seagetech.common.util.SeageUtils;
import com.seagetech.seismic.common.entity.BasedParameter;
import com.seagetech.seismic.common.entity.Device;
import com.seagetech.seismic.common.entity.Task;
import com.seagetech.seismic.common.service.IBasedParameterService;
import com.seagetech.seismic.common.service.ITaskService;
import com.seagetech.seismic.common.utils.Constant;
import com.seagetech.seismic.common.utils.DaqRestTemplate;
import com.seagetech.seismic.common.utils.DaqUtils;
import com.seagetech.seismic.service.monitoring.DeviceService;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 校时job
 *
 * @Author tt_wang
 * @Description //TODO
 * @Date 2020/10/23 14:58
 * @Version 1.0
 **/
@DisallowConcurrentExecution //不允许多线程运行
@Component
public class CheckTimeJob implements Job {

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

    @Autowired
    private ITaskService taskService;

    @Autowired
    private DaqRestTemplate restTemplate;

    @Autowired
    private IBasedParameterService basedParameterService;


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobKey key = jobExecutionContext.getJobDetail().getKey();

        LOGGER.info("校时任务【{}】-【{}】执行开始了--:{}", key.getName(), new Date());
        //获取当前任务
        Task task = taskService.findById(Integer.valueOf(key.getName()));


        //获取校时服务器地址
        BasedParameter obj = (BasedParameter) basedParameterService.getObj(new LambdaQueryWrapper<BasedParameter>().eq(BasedParameter::getParamName, "SNTP服务器"));
        String command;
        if (SeageUtils.isEmpty(obj) || SeageUtils.isEmpty(obj.getParamValue())) {
            command = "04";
        } else {
            command = "04 " + obj.getParamValue();
        }
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
        paramMap.add("command", command);
        String deviceId = "-1";
        if (!SeageUtils.isEmpty(task.getDeviceStr())) {
            deviceId = task.getDeviceStr();
        }

        Map<String, Object> urlPar = new HashMap<>(1);
        urlPar.put("deviceId", deviceId);
        restTemplate.postForObject(DaqUtils.CHECK_TIME, paramMap, String.class, urlPar);

        if (Objects.equals(task.getRepetition(), Constant.REPEAT_ONE)) {
            task.setTaskStatus(3);
            taskService.updateById(task);
        }

    }
}

 

3.实例化job,将job添加到任务管理器

  ps:我这里通过传入的task对象,高度灵活化

package com.seagetech.seismic.common.utils;

import com.seagetech.seismic.common.entity.Task;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class QuartzManager {

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

    @Autowired
    private Scheduler scheduler;

    /**
     * 添加并启动任务
     * @param task
     */
    @SuppressWarnings("unchecked")
    public void addJob(Task task) throws Exception {
        //创建jobDetail实例,绑定job实现类
        Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance()
                .getClass());
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(String.valueOf(task.getTaskId())).usingJobData("taskId",task.getTaskId()).build();
        //定义调度触发规则
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(String.valueOf(task.getTaskId()))// 触发器key
                .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                .withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();
        //把作业和触发器注册到任务调度中
        scheduler.scheduleJob(jobDetail,trigger);
        //启动
        if (!scheduler.isShutdown()){
            scheduler.start();
        }
        LOGGER.info("添加任务"+task.getTaskId()+"到任务调度器");
    }


    /**
     * 暂停一个job
     * @param task
     * @throws SchedulerException
     */
    public void pauseJob(Task task) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(String.valueOf(task.getTaskId()));
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     * @param task
     * @throws SchedulerException
     */
    public void resumeJob(Task task) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(String.valueOf(task.getTaskId()));
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除一个job
     * @param task
     * @throws SchedulerException
     */
    public void deleteJob(Task task) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(String.valueOf(task.getTaskId()));
        scheduler.deleteJob(jobKey);
    }

    /**
     * 立即触发job
     * @param task
     * @throws SchedulerException
     */
    public void runJobNow(Task task) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(String.valueOf(task.getTaskId()));
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新job表达式
     * @param task
     * @throws SchedulerException
     */
    public void updateJobCron(Task task) throws SchedulerException {

        TriggerKey triggerKey = TriggerKey.triggerKey(String.valueOf(task.getTaskId()));

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

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

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

        scheduler.rescheduleJob(triggerKey, trigger);
    }
}

 

task对象如下:

package com.seagetech.seismic.common.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.seagetech.web.validated.annotation.DateTime;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

/**
 * @Author wangZhou
 * @Date 2020/4/24 11:58
 * @company 矽甲(上海)信息科技有限公司
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_task")
public class Task implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 任务ID
     */
    @TableId(value = "task_id", type = IdType.AUTO)
    private Integer taskId;

    /**
     * 控制功能,表tb_device_fun
     */
    @NotNull(message = "validated.paremter_task_funNotNull")
    private Integer deviceFunId;

    /**
     * 重复 1:单次  2:重复
     */
    @NotNull(message = "validated.paremter_task_repetitionNotNull")
    private Integer repetition;

    /**
     * 周期描述,用于列表显示
     */
    private String periodDetail;

    /**
     * 任务状态  0:未开始  1:已启动  2:已停止  3:已过期
     */
    private Integer taskStatus;

    /**
     * 创建时间,格式yyyy-MM-dd HH:mm:ss
     */
    private String createTime;

    /**
     * 节点id列表
     */
    @TableField(exist = false)
    private List<Integer> deviceIds;

    /**
    * 指标主键id列表
    */
    @TableField(exist = false)
    private List<Integer> deviceTargetIds;

    /**
     * 任务执行时调用哪个类的方法 包名+类名
     */
    private String beanClass;

    /**
     * cron表达式
     */
    private String cronExpression;

    /**
     * 设备id Str
     */
    private String deviceStr;



    //执行周期为单次的
    public String runTime;

    @TableField(exist = false)
    private String startTime;


    @TableField(exist = false)
    private String endTime;
}

 

task对象里面有cron表达式,执行的job的类名以及task的id作为job的key,这个key是之后启动、停止、更新、继续job的认证标识

4.自定义jobFactory,方便我们在job类调用spring注入的service,所以要自定义一个jobfactory, 让其在具体job 类实例化时 使用Spring 的API 来进行依赖注入。

package com.seagetech.seismic.manager.timer.comfig;

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;

@Component
public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

 

5.自定义一个监听类,Quartz的job在项目重启时,job都失效了,把每次启动的job都存放在数据库,然后项目启动时监听器读取数据库的job,然后添加job。

package com.seagetech.seismic.manager.timer.comfig;

import com.seagetech.seismic.common.entity.Task;
import com.seagetech.seismic.common.service.ITaskService;
import com.seagetech.seismic.common.utils.QuartzManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Component
@Order(1)
public class ScheduleJobInitListener implements CommandLineRunner {

    @Autowired
    private ITaskService takService;

    @Override
    public void run(String... args) throws Exception {
        initSchedule();
    }


    @Autowired
    private QuartzManager quartzManager;

    @Transactional(readOnly = true,propagation = Propagation.NOT_SUPPORTED)
    public void initSchedule() throws Exception {
        //获取任务信息
        List<Task> all = takService.findAll(new Task().setTaskStatus(JobStatusEnum.RUNNING.getStatus()));
        /**
         * 正在运行的
         */
        for (Task sysTask : all) {
            quartzManager.addJob(sysTask);
        }
    }
}

 

6.启动项目,会自动把数据库的定时任务添加到job中,也可以在web页面添加定时任务,并对定时任务执行各种操作

附:cron工具类

package com.seagetech.seismic.common.utils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author bxf
 * @ClassName: CronUtil
 * @date 2020-6-30 16:09:09
 */
public class CronUtil {

    /**
     * 方法摘要:构建Cron表达式
     *
     * @param type   类型 0:每天一次;1:每周一次;2:每月一次
     * @param param1 下拉列表,根据type进行变换, type=0时,param1=0;type=1时,param1从1-7选;type=2时,param1从1-28选
     * @param param2 小时 0-23
     * @param param3 分钟 0-59
     * @return
     */
    public static String createLoopCronExpression(int type, int param1, int param2, int param3) {
        String cron = "";
        switch (type) {
            case 0:// 每天的param2时,param3分执行
                cron = "0 " + param3 + " " + param2 + " * * ? ";
                break;
            case 1:// 每周的param1的param2时,param3分执行
                cron = "0 " + param3 + " " + param2 + " ? * " + param1 + " *";
                break;
            case 2:// 每月的param1号的param2时,param3分执行
                cron = "0 " + param3 + " " + param2 + " " + param1 + " * ? *";
                break;
        }
        return cron;
    }


    /**
     * 方法摘要:构建Cron表达式
     *
     * @param rate  频率 0秒;1分;2小时;3日;4月
     * @param cycle 周期
     * @return String
     */
    public static String createLoopCronExpression(int rate, int cycle) {
        String cron = "";
        switch (rate) {
            case 0:// 每cycle秒执行一次
                cron = "0/" + cycle + " * * * * ?";
                break;
            case 1:// 每cycle分钟执行一次
                cron = "0 0/" + cycle + " * * * ?";
                break;
            case 2:// 每cycle小时执行一次
                cron = "0 0 0/" + cycle + " * * ?";
                break;
            default:// 默认每cycle秒执行一次
                cron = "0/1 * * * * ?";
                break;
        }
        return cron;

    }



    /**
     * 方法摘要:构建Cron表达式
     *
     * @param rate  频率 0秒;1分;2小时;3日;4月
     * @param cycle 周期
     * @return String
     */
    public static String createLoopCronDescription(int rate, int cycle) {
        String desc = "";
        switch (rate) {
            case 0:
                desc = " 每"+cycle+"秒执行一次";
                break;
            case 1:
                desc = " 每"+cycle+"分钟执行一次";
                break;
            case 2:
                desc = " 每"+cycle+"小时执行一次";
                break;
        }
        return desc;

    }

    /**
     * 方法摘要:构建Cron描述
     *
     * @param type   类型 0:每天一次;1:每周一次;2:每月一次
     * @param param1 下拉列表,根据type进行变换, type=0时,param1=0;type=1时,param1从1-7选;type=2时,param1从1-28选
     * @param param2 小时 0-23
     * @param param3 分钟 0-59
     * @return
     */
    public static String createLoopCronDescription(int type, int param1, int param2, int param3) {
        String desc = "";
        switch (type) {
            case 0:// 每天的param2时,param3分执行
                desc = "每天" + param2 + "点" + param3 + "分触发一次任务";
                break;
            case 1:// 每周的param1的param2时,param3分执行
                desc = "每周" + param1 + "的" + param2 + "点" + param3 + "分触发一次任务";
                break;
            case 2:// 每月的param1号的param2时,param3分执行
                desc = "每月" + param1 + "号的" + param2 + "点" + param3 + "分触发一次任务";
                break;
        }
        return desc;
    }

    /**
     * 通过输入指定日期时间生成cron表达式
     *
     * @param date
     * @return cron表达式
     */
    public static String getCron(Date date) {
        String dateFormat = "ss mm HH dd MM ? *";
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }

        return formatTimeStr;
    }

    // 参考例子
    public static void main(String[] args) {

//        System.out.println(CronUtil.createLoopCronExpression(0, 0, 10, 50));
//        System.out.println(CronUtil.createLoopCronDescription(0, 0, 10, 50));
//
//        System.out.println(CronUtil.createLoopCronExpression(1, 2, 12, 40));
//        System.out.println(CronUtil.createLoopCronDescription(1, 2, 12, 40));
//
//        System.out.println(CronUtil.createLoopCronExpression(2, 30, 07, 30));
//        System.out.println(CronUtil.createLoopCronDescription(2, 30, 07, 30));
//
//        System.out.println(getCron(new Date()));

        System.out.println(createLoopCronExpression(1,3));

    }
}

 

 

参考文献:

https://blog.youkuaiyun.com/l18848956739/article/details/86597709,在此基础上进行改造实现。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值