在項目中有一個需求,需要靈活配置調度任務時間,剛開始用的Java自帶的java.util.Timer類,通過調度一個java.util.TimerTask任務,雖然能夠執行,但是在內部類裏不能調用service,還是有很多不變,後來在網上查了很多資料,雖然本人級別不夠,但是通過各種組合嘗試,終於找到了方法,然後才發現竟是如此簡單,汗顏。。。下面來分享給大家。
1.說明:
spring3.1以下的版本必須使用quartz1.x系列,3.1以上的版本才支持quartz 2.x。在quartz1.x系列中org.quartz.CronTrigger是個類,而在quartz2.x系列中org.quartz.CronTrigger變成了接口,從而造成無法用spring的方式配置quartz的觸發器(trigger)。
我使用的quartz版本是2.2.1 。
最終實現的功能:
1) 可添加新任務,刪除任務,更新任務,暫停任務,恢復任務 ;
2) 動態添加定時任務,按時執行相應的邏輯 ;
2.配置即使用
1)首先我們要一個定時器管理類,這個很重要。所有的定時器操作都要用到這個類。
</pre><pre name="code" class="java">import static org.quartz.JobBuilder.newJob;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
/**
*
* @Description
* @author qgw
* 2016 上午10:05:59 ^_^
*/
public class QuartzManager {
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
private static String JOB_GROUP_NAME = "MY_JOBGROUP_NAME";
private static String TRIGGER_GROUP_NAME = "MY_TRIGGERGROUP_NAME";
/**
* @Description: 添加一個定時任務,使用默認的任務組名,觸發器名,觸發器組名
* @param jobName 任務名
* @param cls 任務
* @param time 時間設置,參考quartz說明文檔
* qgw 2016年1月21日 下午3:30:10 ^_^
*/
@SuppressWarnings("unchecked")
public static void addJob(String jobName, Class cls, String time,Object scheduleJob) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail job = newJob(cls)
.withIdentity(jobName, JOB_GROUP_NAME)
.build();
// 添加具體任務方法
job.getJobDataMap().put("scheduleJob", scheduleJob);
// 表達式調度構建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(time);
// 按新的cronExpression表達式構建一個新的trigger
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(jobName, TRIGGER_GROUP_NAME)
.withSchedule(scheduleBuilder).build();
//交給scheduler去調度
sched.scheduleJob(job, trigger);
// 啓動
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 添加一個定時任務
* @param jobName 任務名
* @param jobGroupName 任務組名
* @param triggerName 觸發器名
* @param triggerGroupName 觸發器組名
* @param jobClass 任務
* @param time 時間設置,參考quartz說明文檔
* qgw 2016年1月21日 下午3:27:00 ^_^
*/
@SuppressWarnings("unchecked")
public static void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail job = newJob(jobClass)
.withIdentity(jobName, jobGroupName)
.build();
// 表達式調度構建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(time);
// 按新的cronExpression表達式構建一個新的trigger
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(triggerName, triggerGroupName)
.withSchedule(scheduleBuilder).build();
sched.scheduleJob(job, trigger);
// 啓動
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一個任務的觸發時間(使用默認的任務組名,觸發器名,觸發器組名)
* @param jobName
* @param time
* qgw 2016年1月21日 下午3:28:34 ^_^
*/
@SuppressWarnings("unchecked")
public static void modifyJobTime(String jobName, String time) {
TriggerKey triggerKey = TriggerKey.triggerKey(
jobName, TRIGGER_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger =(CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
CronScheduleBuilder scheduleBuilder =CronScheduleBuilder.cronSchedule(time);
//按新的cronExpression表達式重新構建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
//按新的trigger重新設置job執行
sched.rescheduleJob(triggerKey, trigger);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一個任務的觸發時間
* @param triggerName
* @param triggerGroupName
* @param time
* @author qgw
* @date 2016年1月27日 下午4:45:15 ^_^
*/
public static void modifyJobTime(String triggerName,
String triggerGroupName, String time) {
TriggerKey triggerKey = TriggerKey.triggerKey(
triggerName, triggerGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
// trigger已存在,則更新相應的定時設置
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
.cronSchedule(time);
// 按新的cronExpression表達式重新構建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
// 按新的trigger重新設置job執行
sched.resumeTrigger(triggerKey);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description 移除一個任務(使用默認的任務組名,觸發器名,觸發器組名)
* @param jobName
* @author qgw
* @date 2016年1月29日 下午2:21:16 ^_^
*/
public static void removeJob(String jobName) {
TriggerKey triggerKey = TriggerKey.triggerKey(
jobName, TRIGGER_GROUP_NAME);
JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
Trigger trigger = (Trigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
sched.pauseTrigger(triggerKey);;// 停止觸發器
sched.unscheduleJob(triggerKey);// 移除觸發器
sched.deleteJob(jobKey);// 刪除任務
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一個任務
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
* @author qgw
* @date 2016年1月29日 下午2:21:16 ^_^
*/
public static void removeJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName) {
TriggerKey triggerKey = TriggerKey.triggerKey(
jobName, triggerGroupName);
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(triggerKey);// 停止觸發器
sched.unscheduleJob(triggerKey);// 移除觸發器
sched.deleteJob(jobKey);// 刪除任務
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:暫停一個任務
* @param jobName
* @param jobGroupName
* qgw 2016年1月22日 下午4:24:55 ^_^
*/
public static void pauseJob(String jobName, String jobGroupName) {
JobKey jobKey =JobKey.jobKey(jobName, jobName);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @Description:暫停一個任務(使用默認組名)
* @param jobName
* @param jobGroupName
* qgw 2016年1月22日 下午4:24:55 ^_^
*/
public static void pauseJob(String jobName) {
JobKey jobKey =JobKey.jobKey(jobName, JOB_GROUP_NAME);
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @Description:啓動所有定時任務
* @author qgw
* @date 2016年1月29日 下午2:21:16 ^_^
*/
public static void startJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description 關閉所有定時任務
* @author qgw
* @date 2016年1月25日 下午2:26:54 ^_^
*/
public static void shutdownJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
這樣我們就可以通過這個類對定時任務做 添加新任務,刪除任務,更新任務,暫停任務,恢復任務。另外說明一點,執行定時任務的類爲傳入的參數Class cls。這個類爲反射出來的類,不歸spring管理,所以在這個類裏注入是不成功的,需要通過spring的上下文獲取bean,並set到構造函數中去進行初始化。調用類舉例:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
/**
* 定時任務運行(反射出來的類)
* @Description
* @author qgw
* 2016 下午2:39:37 ^_^
*/
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {
private static final Logger log = Logger.getLogger("");
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("任務運行開始-------- start --------");
try {
//ScheduleJob任務運行時具體參數,可自定義
ScheduleJob scheduleJob =(ScheduleJob) context.getMergedJobDataMap().get(
"scheduleJob");
}catch (Exception e) {
log.info("捕獲異常==="+e);
}
log.info("任務運行結束-------- end --------");
}
}
2)下面就是調用了
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
/**
* 加載定時任務
* @Description
* @author qgw
* 2016 下午2:24:58 ^_^
*/
@SuppressWarnings("unchecked")
public class LoadTask {
private static final Logger log = Logger.getLogger("");
/**
* @param sendTime 發送時間
* @return
* @author qgw
* @date 2016年1月26日 下午3:39:13 ^_^
*/
public static boolean timerTask(long sendTime,long msgId) {
String cron = QuartzManager.getQuartzTime(Util.toString(sendTime));//獲得quartz時間表達式,此方法自己寫
ScheduleJob job = new ScheduleJob();
String jobName = msgId+"_job";
job.setJobId(msgId);
job.setJobName(jobName);
job.setCreTime(nowTime);
job.setJobCron(cron);
job.setJobTime(sendTime);
job.setJobGroup("MY_JOBGROUP_NAME");
job.setJobDesc(desc);
try {
//刪除已有的定時任務
QuartzManager.removeJob(jobName);
//添加定時任務
QuartzManager.addJob(jobName, QuartzJobFactory.class, cron,job);
return true;
} catch (Exception e) {
log.info("加載定時器錯誤:"+e);
return false;
}
}
}
3.注意
由於定時任務不斷添加,而且不被銷燬,時間長了的話可能會有內存溢出的可能,所以最好還是添加定時任務,把每天過期的定時任務清理一下,個人建議。
4.總結
關於quartz定時器動態添加定時任務的各種方法,要基於項目需求去不斷改進。目前此方法已投入使用,可能還有不完善的地方,希望各位大神來補充。
5.參考