Quartz进阶-工具类封装及代码抽取
前言
经过上篇Quartz进阶-工具类封装及代码抽取之后我们可以添加定时任务了,但删除定时任务还做不到
所以需要工具类来实现–提供动态添加/删除定时任务的方法
另一个问题就是不支持业务的动态扩展
实现方式一:针对每个业务写一个job(太繁琐)
实现方式二:抽取公共的job
1.准备QuartzUtils工具类【quartz.util】
add:添加方法
modify:修改方法
remove:移除方法
/**
* Quartz调度管理器
*
*/
public class QuartzUtils {
private static String JOB_GROUP_NAME = "JOB_GROUP_SYSTEM";
private static String TRIGGER_GROUP_NAME = "TRIGGER_GROUP_SYSTEM";
/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param sched
* 调度器
*
* @param jobName
* 任务名
* @param cls
* 任务
* @param params
* 任务参数
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,
String time) {
try {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);// 任务名,任务组,任务执行类
@SuppressWarnings("unchecked")
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
JobDetail jobDetail = newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build();
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);// 触发器
System.out.println(time);
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();// 启动
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 添加一个定时任务
*
* @param sched
* 调度器
*
* @param jobName
* 任务名
* @param jobGroupName
* 任务组名
* @param triggerName
* 触发器名
* @param triggerGroupName
* 触发器组名
* @param jobClass
* 任务
* @param params
* 任务参数
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass, Object params, String time) {
try {
JobKey jobKey = new JobKey(jobName, jobGroupName);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
@SuppressWarnings("unchecked")
JobDetail jobDetail = newJob(jobClass).withIdentity(jobKey).setJobData(jobDataMap).build();
// 触发器
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();
sched.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
*
* @param sched
* 调度器
* @param jobName
* @param time
*
* @Title: QuartzManager.java
*/
@SuppressWarnings("rawtypes")
public static void modifyJobTime(Scheduler sched, String jobName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
JobDetail jobDetail = sched.getJobDetail(jobKey);
Class objJobClass = jobDetail.getJobClass();
Object params = jobDetail.getJobDataMap().get("params");
removeJob(sched, jobName);
System.out.println("修改任务:" + jobName);
addJob(sched, jobName, objJobClass, params,time);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间
*
* @param sched
* 调度器 *
* @param sched
* 调度器
* @param triggerName
* @param triggerGroupName
* @param time
*
* @Title: QuartzManager.java
*/
public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
// 修改时间
trigger.getTriggerBuilder().withSchedule(cronSchedule(time));
// 重启触发器
sched.resumeTrigger(triggerKey);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*
* @param sched
* 调度器
* @param jobName
*
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务
*
* @param sched
* 调度器
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, jobGroupName);
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:启动所有定时任务
*
* @param sched 调度器
*
* @Title: QuartzManager.java
*/
public static void startJobs(Scheduler sched) {
try {
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:关闭所有定时任务
*
* @param sched
* 调度器
*
*/
public static void shutdownJobs(Scheduler sched) {
try {
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
测试工具类是否正常
//springboot做了自动配置 - 直接注入进来使用即可
@Autowired
private SchedulerFactoryBean factoryBean;
@Test
public void test1 ()throws Exception{
System.err.println(factoryBean);
//1 创建定时任务
Scheduler sched = factoryBean.getScheduler(); //调度器
String jobName = "myJob"; //作业名称
Class cls = PrintTimeJob.class; //作业
// Object params = null; //传null
Object params = "zs"; //传字符串
// Object params = new User(1L,"zs"); //传对象 - 对象需要序列化
String time = "0/1 * * * * ?"; //触发时间 - Cron表达式
QuartzUtils.addJob(sched,jobName,cls,params,time);
System.out.println("作业已经创建...");
//2 睡眠一下
Thread.sleep(10000);
//3 删除定时任务
QuartzUtils.removeJob(sched,jobName);
System.out.println("作业已经删除...");
//让程序不结束 - 删除就不会执行定时任务了
Thread.sleep(10000);
}
业务分析
虽然有了QuartzUtil
但是所有订单中都要写这样一堆代码
Scheduler sched = factoryBean.getScheduler(); //调度器
String jobName = "myJob"; //作业名称
Class cls = PrintTimeJob.class; //作业
// Object params = null; //传null
Object params = "zs"; //传字符串 //传对象
// Object params = new User(1L,"zs"); //传字符串 //传对象
String time = "0/1 * * * * ?"; //触发时间 - Cron表达式 QuartzUtils.addJob(sched,jobName,cls,params,time);
解决:写一个service将代码封装到里面,然后其他地方注入之后直接调用即可
而且添加的时候都要定义一个类似于PrintTimeJob类这会导致类会越来越多扩展性很差
解决:写一个MainJob,使用类型type区分
2.准备QuartzJobInfo对象【quartz.dto】
将工具类的参数封装,方便进行传递,参数就是测试工具类时必须要的参数
作用:
将QuartzUtils工具类中的add方法的参数进行封装
将Date类型的时间转换成cron表达式
type
@Data
public class QuartzJobInfo implements Serializable {
//业务类型:超时未支付,超时未确认
private String type;
//作业名
private String jobName;
//参数
private Map<String, Object> params;
//cron表达式
private String cronj;
private Date fireDate;
//将一个Date时间类型转换成一个cron表达式并赋值给当前对象的cronj
public void setFireDate(Date fireDate) {
this.fireDate = fireDate;
String[] cronArr = new String[7];
for (int i = 0; i < cronArr.length; i++) {
cronArr[i] = "";
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(fireDate);
int second = calendar.get(Calendar.SECOND);
int minute = calendar.get(Calendar.MINUTE);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int month = calendar.get(Calendar.MONTH) + 1;
int year = calendar.get(Calendar.YEAR);
cronArr[0] = second + "";
cronArr[1] = minute + "";
cronArr[2] = hour + "";
cronArr[3] = day + "";
cronArr[4] = month + "";
cronArr[5] = "?";
cronArr[6] = year + "";
String cron = String.join(" ",cronArr).trim();
this.setCronj(cron);
}
//测试
public static void main(String[] args) {
QuartzJobInfo quartzJobInfo = new QuartzJobInfo();
quartzJobInfo.setFireDate(new Date(System.currentTimeMillis()+60*60*1000));
System.out.println(quartzJobInfo.getCronj());
}
}
3.准备JobConstants常量类【quartz.constant】 - 拷贝
作用:用来区分不同的业务类型【列:8个 = 4个取消 + 4个自动确认(4个订单)】
超时未支付 - 取消订单【改状态】
超时未确认 - 自动确认【改状态】
/**
* 作业相关常量
*/
public class JobConstants {
//超时未支付
//领养订单超时未支付
public static final String OVER_TIME_NO_PAY_ADOPT = "over_time_no_pay_adopt";
//服务订单超时未支付
public static final String OVER_TIME_NO_PAY_PRODUCT = "over_time_no_pay_product";
//商品订单超时未支付
public static final String OVER_TIME_NO_PAY_GOODS = "over_time_no_pay_goods";
//充值订单超时未支付
public static final String OVER_TIME_NO_PAY_RECHARGE = "over_time_no_pay_recharge";
//超时未确认
public static final String OVER_TIME_NO_CONFIRM_ADOPT = "over_time_no_confirm_adopt";
public static final String OVER_TIME_NO_CONFIRM_PRODUCT = "over_time_no_confirm_product";
public static final String OVER_TIME_NO_CONFIRM_GOODS = "over_time_no_confirm_goods";
public static final String OVER_TIME_NO_CONFIRM_RECHARGE = "over_time_no_confirm_recharge";
}
4.写一个MainJob实现Job接口【quartz.job】
public class MainJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//根据不同的类型 type 区分不同的业务
}
}
至此准备工作完毕
5.编写QuartzServiceImpl
目的:为了简写调用添加和删除定时器的方法
public interface IQuartzUtils {
/**
* 添加定时任务-对工具类的add定时任务的方法再次封装
*
* @param info
*/
void add(QuartzJobInfo info);
/**
* 移除定时任务
* @param jobName
*/
void remove(String jobName);
}
//目的:为了简写调用添加和删除定时器的方法
@Service
public class QuartzServiceImpl implements IQuartzService {
//springboot做了自动配置 - 直接注入进来使用即可
@Autowired
private SchedulerFactoryBean factoryBean;
@Override
public void add(QuartzJobInfo info) {
QuartzUtils.addJob(
factoryBean.getScheduler(),
info.getJobName(),
MainJob.class,
info,
info.getCronj());
}
@Override
public void remove(String jobName) {//常量类 + unionPaySn
QuartzUtils.removeJob(factoryBean.getScheduler(),jobName);
}
}
下列为项目功能代码块(忽略)
添加定时任务:
1.在生成订单,跳转到支付页面的时候就要添加定时任务【AdoptOrderServiceImpl】
//4.创建订单倒计时 - 15分钟未支付就取消订单 @TODO
QuartzJobInfo info = new QuartzJobInfo();
info.setType(JobConstants.OVER_TIME_NO_PAY_ADOPT);
info.setFireDate(payBill.getLastPayTime());
info.setJobName(JobConstants.OVER_TIME_NO_PAY_ADOPT + payBill.getUnionPaySn());
Map<String,Object> map = new HashMap<>();
map.put("orderId",adoptOrder.getId());
info.setParams(map);
quartzService.add(info);
System.out.println("订单倒计时取消任务【" + info.getJobName() + "】创建成功...");
return payData;
2.MainJob中使用type区分不同的取消任务【MainJob】
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// jobDataMap.put("params", params);
// 获取传递的参数:info 就可以强转成info //添加定时任务最底层就是调用工具类,工具类中添加参数put("params", params);
QuartzJobInfo info = (QuartzJobInfo)jobExecutionContext.getJobDetail().getJobDataMap().get("params");
if(info.getType().equals(JobConstants.OVER_TIME_NO_PAY_ADOPT)){ //领养订单超时未支付
//获取参数 - 修改订单或支付单的状态 - 找到支付单或订单【id,paySn】
Map<String, Object> params = info.getParams();
Long orderId = Long.parseLong(params.get("orderId").toString());
//修改订单状态
AdoptOrder adoptOrder = adoptOrderMapper.loadById(orderId);
adoptOrder.setState(-1); //取消订单
adoptOrderMapper.update(adoptOrder);
//修改支付单状态
PayBill payBill = payBillMapper.loadByUnionPaySn(adoptOrder.getPaySn());
payBill.setState(-1);
payBillMapper.update(payBill);
}
}
3.支付成功之后,删除定时任务【PayController】
//修改订单状态 if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_ADOPT)){ //修改的是领养订单 - 找到是哪一种订单
AdoptOrder adoptOrder = adoptOrderService.getById(payBill.getBusinessKey());
adoptOrder.setState(1);
adoptOrderService.update(adoptOrder);
//支付成功之后 取消定时任务
quartzService.remove(JobConstants.OVER_TIME_NO_PAY_ADOPT + out_trade_no);
System.out.println("定时任务移除成功...");
}else if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_PRODUCT)){//修改服务订单 @TODO
}
2.MainJob中使用type区分不同的取消任务【MainJob】
public class MainJob implements Job {
@Autowired
private AdoptOrderMapper adoptOrderMapper;
@Autowired
private PayBillMapper payBillMapper;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//根据不同的类型 type 区分不同的业务
// jobDataMap.put("params", params);
// 获取传递的参数:info 就可以强转成info //添加定时任务最底层就是调用工具类,工具类中添加参数put("params", params);
QuartzJobInfo info = (QuartzJobInfo)jobExecutionContext.getJobDetail().getJobDataMap().get("params");
if(info.getType().equals(JobConstants.OVER_TIME_NO_PAY_ADOPT)){ //领养订单超时未支付
//获取参数 - 修改订单或支付单的状态 - 找到支付单或订单【id,paySn】
Map<String, Object> params = info.getParams();
Long orderId = Long.parseLong(params.get("orderId").toString());
//修改订单状态
AdoptOrder adoptOrder = adoptOrderMapper.findById(orderId);
adoptOrder.setState(-1); //取消订单
adoptOrderMapper.update(adoptOrder);
//修改支付单状态
PayBill payBill = payBillMapper.loadByUnionPaySn(adoptOrder.getPaySn());
payBill.setState(-1);
payBillMapper.update(payBill);
}
}
}
3.支付成功之后,删除定时任务【AlipayController】
//修改订单状态
if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_ADOPT)){//确认是领养订单,因为其他订单有可能有一样的id值-找到是哪一种订单
Long adoptOrderId = payBill.getBusinessKey();
AdoptOrder adoptOrder = adoptOrderService.findById(adoptOrderId);
adoptOrder.setState(1);
//修改订单的状态
adoptOrderService.update(adoptOrder);
//支付成功之后 取消定时任务
quartzService.remove(JobConstants.OVER_TIME_NO_PAY_ADOPT + out_trade_no);
System.out.println("定时任务移除成功...");
}else if (payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_RECHARGE)){//修改服务订单的状态 @todo
}