(一)、封裝工具類
??上一篇說明了一下Quartz的下載和安裝,接下來說具體怎么使用,為了能盡快在項目中使用,下面貼出一個封裝好的工具類,以后可以根據情況自己進行封裝。QuartzUtils文件內容:
package com.common.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Component;
/**
* Quqrtz任務管理類
* @version 1.0
*/
public class QuartzUtils {
/** 默認任務組名稱 */
public static final String JOB_GROUP_NAME = "DEFAULT_JOB_GROUP_NAME";
/** 默認觸發器組名稱 */
public static final String TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP_NAME";
//spring bean中配置的任務調度器實例 這里直接使用
private static Scheduler scheduler;
static{
// 此種方式是從Spring中獲取bean
scheduler = SpringContextHolder.getBean("quartzScheduler");
// 下面這種方式是讓Quartz根據quartz.properties配置文件手動初始化調度器,quartz.properties文件放在classpath目錄下
/*
StdSchedulerFactory factory = new StdSchedulerFactory();
try {
factory.initialize("quartz.properties");
scheduler = factory.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
throw new RuntimeException("scheduler init fail.");
}
*/
}
/**
* 檢查任務是否啟動
* @param jobName 任務名
* @param jobGroupName 任務組名稱
*/
public static boolean checkJobIsExists(String jobName, String jobGroupName) throws SchedulerException {
String[] jobNames = scheduler.getJobNames(jobGroupName);
List<String> jobNameList = Arrays.asList(jobNames);
return jobNameList.contains(jobName);
}
/**
* 添加一個定時任務(標準)
* @param jobName 任務名
* @param cls 任務執行類
* @param time 時間設置
* @return 任務啟動結果
*/
public static void addJob(String jobName, Class<? extends Job> cls, String time) throws RuntimeException{
try {
// 任務名,任務組,任務執行類
JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);
jobDetail.setRequestsRecovery(true);
// 創建觸發器
CronTrigger trigger = new CronTrigger(jobDetail.getName(), TRIGGER_GROUP_NAME); // 觸發器名,觸發器組
trigger.setCronExpression(time); // 觸發器時間設定
// 調度任務
if(!scheduler.isShutdown()) {
scheduler.scheduleJob(jobDetail, trigger);
}
// 啟動調度器
if (!scheduler.isStarted()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("添加一個定時任務失敗: 方法(addJob) 失敗原因:" + e.getMessage());
}
}
/**
* 添加一個定時任務 可以將任何對象保存到此Job中(將對象放到jobDataMap中,map中的key為jobName)
* @param jobName 任務名
* @param cls 任務執行類
* @param time 時間設置
* @param obj 需要保存的對象
* @return 啟動結果
*/
public static void addJobAndData(String jobName, Class<? extends Job> cls, String time, Object obj) throws RuntimeException {
try {
// 任務名,任務組,任務執行類
JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);
jobDetail.setRequestsRecovery(true);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put(jobName, obj);
jobDetail.setJobDataMap(jobDataMap);
// 創建觸發器
CronTrigger trigger = new CronTrigger(jobDetail.getName(), TRIGGER_GROUP_NAME); // 觸發器名,觸發器組
trigger.setCronExpression(time); // 觸發器時間設定
// 調度任務
if(!scheduler.isShutdown()) {
scheduler.scheduleJob(jobDetail, trigger);
}
// 啟動調度器
if (!scheduler.isStarted()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("添加一個定時任務失敗: 方法(addJobAndData) 失敗原因:" + e.getMessage());
}
}
/**
* 暫停一個任務
* @param jobName 任務名稱
* @return 暫停結果
*/
public static void pauseJob(String jobName) throws RuntimeException {
try {
scheduler.pauseTrigger(jobName, TRIGGER_GROUP_NAME); // 停止觸發器
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("暫停一個定時任務失敗: 方法(pauseJob) 失敗原因:" + e.getMessage());
}
}
/**
* 恢復一個任務
* @param jobName 任務名稱
* @return 恢復結果
*/
public static void resumeJob(String jobName) throws RuntimeException {
try {
scheduler.resumeTrigger(jobName, TRIGGER_GROUP_NAME); // 恢復觸發器
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("恢復一個定時任務失敗: 方法(resumeJob) 失敗原因:" + e.getMessage());
}
}
/**
* 刪除一個任務
* @param jobName 任務名稱
* @return 刪除結果
*/
public static void removeJob(String jobName) throws RuntimeException {
try {
scheduler.pauseTrigger(jobName, TRIGGER_GROUP_NAME); // 停止觸發器
scheduler.unscheduleJob(jobName, TRIGGER_GROUP_NAME); // 移除觸發器
scheduler.deleteJob(jobName, JOB_GROUP_NAME); // 刪除任務
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("刪除一個定時任務失敗: 方法(removeJob) 失敗原因:" + e.getMessage());
}
}
/**
* 啟動所有定時任務(啟動調度器)
* @return 啟動結果
*/
public static void startJobs() throws RuntimeException{
try {
scheduler.start();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("啟動所有定時任務失敗: 方法(startJobs) 失敗原因:" + e.getMessage());
}
}
/**
* 關閉所有定時任務(關閉調度器)
* @return 啟動結果
*/
public static void shutdownJobs() throws RuntimeException{
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException("關閉所有定時任務失敗: 方法(shutdownJobs) 失敗原因:" + e.getMessage());
}
}
/** 當前時間常量標識 */
public static final String CURR_TIME = "CURR_TIME";
/**將指定時間加上N天 然后計算出Quartz定時的時間表達式(eg: "0 22 14 12 8 ? 2015") */
public static String countQuartzTimeAndDays(String dateTime, int days) throws RuntimeException{
//將字符串轉化為日期對象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
if(CURR_TIME.equals(dateTime)){
date = new Date();
}else{
try {
date = format.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("計算時間表達式失敗: 方法(countQuartzTimeAndDays) 失敗原因:" + e.getMessage());
}
}
Calendar currCalendar = Calendar.getInstance(); //當前的時間
currCalendar.setTime(date);
currCalendar.add(Calendar.DAY_OF_MONTH, days);
return countQuartzTime(format.format(currCalendar.getTime()));
}
/**將指定時間加上N秒 然后計算出Quartz定時的時間表達式(eg: "0 22 14 12 8 ? 2015") */
public static String countQuartzTimeAndSeconds(String dateTime, int seconds) throws RuntimeException{
//將字符串轉化為日期對象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
if(CURR_TIME.equals(dateTime)){
date = new Date();
}else{
try {
date = format.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("計算時間表達式失敗: 方法(countQuartzTimeAndSeconds) 失敗原因:" + e.getMessage());
}
}
Calendar currCalendar = Calendar.getInstance(); //當前的時間
currCalendar.setTime(date);
currCalendar.add(Calendar.SECOND, seconds);
return countQuartzTime(format.format(currCalendar.getTime()));
}
/**
* 根據時間 計算出Quartz定時的時間表達式(eg: "0 22 14 12 8 ? 2015")
* @param time 定時時間字符串 精確到時分秒 注意: 不能為過去時
* @return 返回Quartz指定的時間表達式 如果轉換失敗 返回null
*/
public static String countQuartzTime(String time) throws RuntimeException{
Calendar sendCalendar = Calendar.getInstance(); //定時時間
Calendar currCalendar = Calendar.getInstance(); //當前時間
//將字符串轉化為日期對象
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date sendDate = null;
try {
sendDate = format.parse(time);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("計算時間表達式失敗: 方法(countQuartzTime) 失敗原因:" + e.getMessage());
}
sendCalendar.setTime(sendDate);
currCalendar.setTime(new Date());
if(sendCalendar.after(currCalendar)){
int seconds = sendCalendar.get(Calendar.SECOND);
int minutes = sendCalendar.get(Calendar.MINUTE);
int hours = sendCalendar.get(Calendar.HOUR_OF_DAY);
int day = sendCalendar.get(Calendar.DAY_OF_MONTH);
int month = sendCalendar.get(Calendar.MONTH) + 1;
int year = sendCalendar.get(Calendar.YEAR);
//拼接表達式
StringBuffer sb = new StringBuffer();
sb.append(seconds).append(" ").append(minutes).append(" ").append(hours).append(" ").append(day)
.append(" ").append(month).append(" ").append("?").append(" ").append(year);
return sb.toString();
}
throw new RuntimeException("計算時間表達式失敗: 方法(countQuartzTime) 失敗原因: 參數時間已是過時時間,必須是未來時間!");
}
}
后面的篇章會講到Quartz具體的內部實現細節,現在先從最簡單添加一個Job任務開始。
(二)、添加一個簡單的job任務
1、第一步創建一個java類,實現org.quartz.Job接口,重寫execute()方法,示例:
package com.modules.monitor.timer;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DemoJob implements Job{
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("demo 演示");
}
}
??org.quartz.Job接口是一個無狀態的任務接口,實現此接口的任務每當觸發器觸發時都會準時執行,調度器并不關心此任務是否有其他實例正在運行,如果任務存在阻塞,可能會導致大量的任務實例并行執行。如果業務中存在不允許并行執行的任務,此時就用到有狀態的org.quartz.StatefulJob接口,此接口的任務實現類不能并行執行,必須等上一次觸發器觸發執行完成后 才可以進行下一次觸發執行。
2、第二步調用QuartzUtils.java中addJob方法
public static void addJob(String jobName, Class<? extends Job> cls, String time)
參數:
- String jobName是任務的名稱,建議使用"Job類名+uuid"作為名稱,保證可快速識別和保證唯一;
- Class cls是需要執行的job類的class對象,例如上面的DemoJob.class;
- time是時間表達式,可以使用QuartzUtils中的countQuartzTime方法計算出表達式
大家也可以通過@在線Cron表達式生成器網站生成
調用示例:
String timeCron = QuartzUtils.countQuartzTime("2019-1-18 10:45:57");
QuartzUtils.addJob("DemoJob-uuid",DemoJob.class, timeCron);
調用完成!就可以等觸發器觸發執行任務了。
(三)、添加一個帶參數的job任務
1、第一步創建一個java類,實現org.quartz.Job接口,重寫execute()方法,示例:
package com.modules.monitor.timer;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DemoJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
// 任務詳情類
JobDetail jobDetail = context.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
// get方法中的key名稱使用jobDetail.getName()是因為addJobAndData方法封裝的
// 自己可以定義其他的key名稱
String msg = (String) jobDataMap.get(jobDetail.getName());
// 打印傳遞過來的參數
System.out.println(msg);
}
}
2、第二步調用QuartzUtils.java中addJobAndData方法
public static void addJobAndData(String jobName, Class<? extends Job> cls, String time, Object obj)
參數:
- 其他參數在上面已講解到
- Object obj是需要傳遞的參數
調用示例:
String timeCron = QuartzUtils.countQuartzTime("2019-1-18 10:45:57");
QuartzUtils.addJob("DemoJob-uuid",DemoJob.class, timeCron, "我是一段任務執行時打印的文本");
調用完成!這里傳遞參數的核心載體是org.quartz.JobDataMap類,將此類當做java中的Map來使用就行。