最近在做一個定時相關的功能,選擇使用quartz來完成。這個過程中自己封裝了一個QuartzUtil工具類,并且一步步完善,填了很多坑。相比于其他類似的文章,個人感覺我寫的這個工具類還算是比較完善的一個了。
我也寫了一個簡單的demo項目,用來幫助你更好的了解,公衆号回複【github】擷取項目github位址,該項目我會一直保持更新。
主要實作功能如下:
- 基于quartz2.3.1實作動态管理定時任務。
- 使用swagger實作接口文檔。
- 前後端統一使用JSON格式互動。
當然也可以直接帶走這個工具類,類中使用了我自定義的一個異常類和一個枚舉類,都一起放在下面了。個人并未配置quartz.properties,使用時隻需添加quartz依賴,然後将quartzUtil和兩個相關的類放到項目中即可。
一、項目swagger文檔
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yM3E2YkdTNiZDZxUGMzEjZ3ImZwMzY1YGN3Y2Y5YGN28CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
二、項目Maven依賴
很巧都是基于2.3.1版本。
(1) springboot版本
org.springframework.boot spring-boot-starter-parent 2.3.1.RELEASE
(2) quartz依賴
org.quartz-scheduler quartz 2.3.1
三、quartz工具類
在QuartzUtil中用到了我自定義的異常類CustomException和枚舉類Code。
(1) CustomException類
/** * @author frost2 * @date 2020-9-23 17:23:38 */@[email protected]@Setterpublic class CustomException extends RuntimeException { public CustomException(int code, String msg) { this.code = code; this.msg = msg; } public CustomException(Code code) { this.code = code.getCode(); this.msg = code.getMsg(); } private int code; private String msg;}
(2) Code類
為了不讓篇幅太長,我隻保留QuartzUtil中用到的狀态碼。
/** * @author frost2 * @date 2020-9-23 17:23:43 */@[email protected] enum Code { PARAM_FORMAT_ERROR(2004, "參數異常"), EXECUTION_ERROR(6002, "接口執行失敗"); //枚舉的屬性字段必須是私有且不可變 private final int code; private final String msg; Code(int code, String msg) { this.code = code; this.msg = msg; }}
(3) QuartzUtil 類
/** * @author frost2 * @date 2020-9-17 15:38:43 */public class QuartzUtil { private static SchedulerFactory schedulerFactory = new StdSchedulerFactory(); private static Scheduler scheduler; static { try { scheduler = schedulerFactory.getScheduler(); } catch (SchedulerException e) { throw new CustomException(Code.EXECUTION_ERROR); } } /** * 傳入:任務名稱、觸發器名稱、任務描述、要執行的任務類、cron表達式建立定時任務, * 傳回是否建立成功 * * 注: * 在建立任務時未設定jobGroup和triggerGroup,Job建立後其均為預設值:DEFAULT, * 是以新建立的任務的jobName和triggerName,均不能與之前任務的重複. * * @param jobName 任務名 * @param triggerName 觸發器名 * @param description 對該任務的秒數(非必須) * @param jobClass 要執行的任務 * @param cron cron表達式 * @return true:建立Job成功,false:建立Job失敗 */ public static boolean addJob(String jobName, String triggerName, String description, Class jobClass, String cron) { try { JobDetail jobDetail = JobBuilder.newJob(jobClass) .withIdentity(jobName) .withDescription(description)// .usingJobData("param1", "将參數")// .usingJobData("param2", "傳遞到JOB任務中使用") .build(); CronTrigger cronTrigger = TriggerBuilder.newTrigger() .withIdentity(triggerName) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); scheduler.scheduleJob(jobDetail, cronTrigger); if (!scheduler.isShutdown()) { scheduler.start(); } return scheduler.isStarted(); } catch (Exception e) { throw new CustomException(Code.EXECUTION_ERROR); } } /** * 修改一個任務的觸發時間 * * @param jobName 任務名稱 * @param triggerName 觸發器名 * @param cron cron表達式 * @return true:修改Job成功,false:修改Job失敗 */ public static Boolean rescheduleJob(String jobName, String triggerName, String cron) { try { TriggerKey triggerKey = TriggerKey.triggerKey(triggerName); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { return false; } checkJobNameAndTriggerName(jobName, trigger); Date latestFireTime = new Date(); if (!trigger.getCronExpression().equalsIgnoreCase(cron)) { trigger = TriggerBuilder.newTrigger() .withIdentity(triggerName) .startNow() .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); //rescheduleJob()執行成功傳回最近一次執行的時間,如果失敗傳回null latestFireTime = scheduler.rescheduleJob(triggerKey, trigger); } return null != latestFireTime; } catch (SchedulerException e) { throw new CustomException(Code.EXECUTION_ERROR); } } /** * 根據jobName和triggerName删除該JOB * * @param jobName 任務名稱 * @param triggerName 觸發器名 * @return true:删除Job成功,false:删除Job失敗 */ public static boolean removeJob(String jobName, String triggerName) { boolean flag; try { Trigger trigger = scheduler.getTrigger(new TriggerKey(triggerName)); if (null == trigger) { return false; } checkJobNameAndTriggerName(jobName, trigger); TriggerKey triggerKey = trigger.getKey(); scheduler.pauseTrigger(triggerKey); flag = scheduler.unscheduleJob(triggerKey); //删除trigger之後無需在删除job,因為相關的job如果不是持久的,則将被自動删除。下面這種寫法flag=false// if (flag) {// flag = scheduler.deleteJob(JobKey.jobKey(jobName));// } } catch (SchedulerException e) { throw new CustomException(Code.EXECUTION_ERROR); } return flag; } /** * 根據jobName和triggerName查詢該JOB * * @param jobName 任務名稱 * @param triggerName 觸發器名 * @return 該Job相關資訊[詳見getJobInfo方法] */ public static HashMap getJob(String jobName, String triggerName) { try { JobDetail jobDetail = scheduler.getJobDetail(new JobKey(jobName)); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(new TriggerKey(triggerName)); if (null == jobDetail || null == trigger) { return new HashMap<>(); } checkJobNameAndTriggerName(jobName, trigger); return getJobInfo(jobDetail, trigger); } catch (SchedulerException e) { throw new CustomException(Code.EXECUTION_ERROR); } } /** * 查詢所有正在執行的JOB * * @return 該Job相關資訊[詳見getJobInfo方法] */ public static List> getJobs() { List> list = new ArrayList<>(); try { List triggerGroupNames = scheduler.getTriggerGroupNames(); for (String groupName : triggerGroupNames) { GroupMatcher groupMatcher = GroupMatcher.groupEquals(groupName); //擷取所有的triggerKey Set triggerKeySet = scheduler.getTriggerKeys(groupMatcher); for (TriggerKey triggerKey : triggerKeySet) { //擷取CronTrigger CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); //擷取trigger對應的JobDetail JobDetail jobDetail = scheduler.getJobDetail(trigger.getJobKey()); list.add(getJobInfo(jobDetail, trigger)); } } } catch (SchedulerException e) { throw new CustomException(Code.EXECUTION_ERROR); } return list; } /** * @param cron cron表達式 * @return 最近5次的執行時間 */ public static List getRecentTriggerTime(String cron) { List list = new ArrayList<>(); try { CronTriggerImpl cronTriggerImpl = new CronTriggerImpl(); cronTriggerImpl.setCronExpression(cron); List dateList = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 5); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateList.forEach(date -> list.add(dateFormat.format(date))); } catch (ParseException e) { throw new CustomException(Code.EXECUTION_ERROR); } return list; } /** * 校驗cron表達式是否正确 * * @param cronExpression cron表達式 * @return true:正确,false:不正确 */ @SuppressWarnings("all") public static boolean checkCronExpression(String cronExpression) { return CronExpression.isValidExpression(cronExpression); } /** * 擷取該Job對應的相關資訊 */ private static HashMap getJobInfo(JobDetail jobDetail, CronTrigger trigger) { HashMap map = new HashMap<>(); map.put("jobName", jobDetail.getKey().getName()); map.put("jobGroup", jobDetail.getKey().getGroup()); map.put("corn", trigger.getCronExpression()); map.put("triggerName", trigger.getKey().getName()); map.put("description", jobDetail.getDescription()); return map; } /** * 校驗jobName和triggerName是否比對 * 如不比對抛出自定義異常 */ private static void checkJobNameAndTriggerName(String jobName, Trigger trigger) { String name = trigger.getJobKey().getName(); if (!name.equals(jobName)) { throw new CustomException(Code.PARAM_FORMAT_ERROR.getCode(), "jobName與triggerName不比對"); } }}
三、關注回複【有驚喜】