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,在此基础上进行改造实现。。。