Spring+Quartz實現(xiàn)動態(tài)任務調度詳解
一、定時任務的實現(xiàn)
1、配置文件方式
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="userTimer" class="com.kevin.timer.XmlTimer"></bean> <bean id="userTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="userTimer" /> </property> <property name="targetMethod"> <value>execute</value> </property> </bean> <!-- 每隔5秒鐘執(zhí)行一次,我要在運行時動態(tài)修改為每小時執(zhí)行一次 --> <bean id="userTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail"> <ref bean="userTask" /> </property> <property name="cronExpression"> <value>0/5 * * * * ?</value> </property> </bean> <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="userTrigger" /> </list> </property> </bean> </beans>
2、注解方式
我最近經?;趕pring boot寫定時任務,并且是使用注解的方式進行實現(xiàn),分成的方便將自己的類注入spring容器(@conponent等),
并在要實現(xiàn)父方法上添加注解 @Scheduled(cron="0/10 * * * * ?") ,告知任務調度器質性方法中的業(yè)務邏輯的質性時間。
二、任務調用的工作原理

三、動態(tài)任務調用的實現(xiàn)
首先定義任務實體應該具有的屬性:
package com.kevin.schedule.entity;
/**
* 調度任務定義
* @author Tom
*
*/
public class Task {
private String id; //任務ID,默認系統(tǒng)時間戳
private String parentId = ""; //父級任務ID
private String name = ""; //任務名稱
private String desc = ""; //任務描述
private int planExe = 0; //計劃執(zhí)行次數,默認為0,表示滿足條件循環(huán)執(zhí)行
private String group = ""; //任務組名稱
private String groupDesc = ""; //任務組描述
private String cron = ""; //任務表達式
private String cronDesc = ""; //表達式描述
private String trigger = ""; //觸發(fā)器
private String triggerDesc = "";//觸發(fā)器描述
private int execute = 0; //任務被執(zhí)行過多少次
private Long lastExeTime = 0L; //最后一次開始執(zhí)行時間
private Long lastFinishTime = 0L;//最后一次執(zhí)行完成時間
private int state = 1; //任務狀態(tài)0禁用、1啟動、2刪除
private int deply = 0; //延時啟動,默認為0,表示不延時啟動
public Task(String taskId){
this.id = taskId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
public String getCronDesc() {
return cronDesc;
}
public void setCronDesc(String cronDesc) {
this.cronDesc = cronDesc;
}
// public List<ScheduleJob> getChildren() {
// return children;
// }
// public void setChildren(List<ScheduleJob> children) {
// this.children = children;
// }
public int getExecute() {
return execute;
}
public void setExecute(int execute) {
this.execute = execute;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getGroupDesc() {
return groupDesc;
}
public void setGroupDesc(String groupDesc) {
this.groupDesc = groupDesc;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Long getLastExeTime() {
return lastExeTime;
}
public void setLastExeTime(Long lastExeTime) {
this.lastExeTime = lastExeTime;
}
public String getTrigger() {
return trigger;
}
public void setTrigger(String trigger) {
this.trigger = trigger;
}
public String getTriggerDesc() {
return triggerDesc;
}
public void setTriggerDesc(String triggerDesc) {
this.triggerDesc = triggerDesc;
}
public int getDeply() {
return deply;
}
public void setDeply(int deply) {
this.deply = deply;
}
public int getPlanExe() {
return planExe;
}
public void setPlanExe(int planExe) {
this.planExe = planExe;
}
// public String getTriggerGroup() {
// return triggerGroup;
// }
// public void setTriggerGroup(String triggerGroup) {
// this.triggerGroup = triggerGroup;
// }
// public String getTriggerGroupDesc() {
// return triggerGroupDesc;
// }
// public void setTriggerGroupDesc(String triggerGroupDesc) {
// this.triggerGroupDesc = triggerGroupDesc;
// }
public Long getLastFinishTime() {
return lastFinishTime;
}
public void setLastFinishTime(Long lastFinishTime) {
this.lastFinishTime = lastFinishTime;
}
}package com.kevin.schedule.proxy;
import java.lang.reflect.Method;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.gupaoedu.schedule.entity.Task;
/**
*
* @author Tom
*
*/
public class TriggerProxy implements Job{
public static final String DATA_TARGET_KEY = "target"; //目標對象,實例
public static final String DATA_TRIGGER_KEY = "trigger"; //方法名
public static final String DATA_TRIGGER_PARAMS_KEY = "trigger_params";//方法的參數值
public static final String DATA_TASK_KEY = "task"; //自己封裝的任務對象
private ThreadLocal<Entry> local = new ThreadLocal<Entry>();
//是由調度器自動調用的
public void execute(JobExecutionContext context) throws JobExecutionException {
// TriggerProxy.class.getResource("")
try {
local.set(new Entry());
//獲取參數信息
JobDataMap data = context.getTrigger().getJobDataMap();
Object target = data.get(DATA_TARGET_KEY);
Method method = (Method)data.get(DATA_TRIGGER_KEY);
Object[] params = (Object[])data.get(DATA_TRIGGER_PARAMS_KEY);
//修改任務執(zhí)行次數
Task task = (Task)data.get(DATA_TASK_KEY);
//任務沒執(zhí)行一次,需要累加1
task.setExecute(task.getExecute() + 1);
local.get().start = System.currentTimeMillis();
//調用觸發(fā)器,用反射調用我們自己定義的方法
method.invoke(target,params);
local.get().end = System.currentTimeMillis();
//記錄任務的最后一次執(zhí)行時間
task.setLastExeTime(local.get().start);
//記錄任務完成的時間
task.setLastFinishTime(local.get().end);
} catch (Exception e) {
e.printStackTrace();
}
}
class Entry{
public long start = 0L;
public long end = 0L;
}
}@Component
public class AnnotationTimer {
Logger LOG = Logger.getLogger(this.getClass());
@Scheduled(cron="0/10 * * * * ?")
@Async
public void a(){
LOG.info("annotation配置的任務執(zhí)行");
}
}定義動態(tài)任務調度需要的方法,在web項目中,通過界面的方式調用方法以動態(tài)控制和查看任務調度的執(zhí)行情況:
package com.kevin.schedule.service;
import java.util.List;
import com.gupaoedu.schedule.entity.Task;
public interface IScheduleService {
/**
* 獲取任務列表
* @return
*/
public List<Task> getAllTask();
/**
* 根據任務ID獲取一個任務
* @return
*/
public Task getTask(String taskId);
/**
* 新建一個任務
* @param taskName 任務名稱
* @param taskClassName 任務Class名稱
* @param triggerName 觸發(fā)器名稱
* @param cron 執(zhí)行表達式
* @throws Exception
*/
public Task createTask(String taskName,String taskClassName,String triggerName,String cron) throws Exception;
/**
* 修改一個任務的觸發(fā)時間(使用默認的任務組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task modifyTaskCron(String taskId, String cron);
/**
* 移除一個任務(使用默認的任務組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task removeTask(String taskId);
/**
* 重啟任務
* @param taskId
* @return
*/
public Task restartTask(String taskId);
/**
* 暫停定時任務
* @param taskId
* @return
*/
public Task pauseTask(String taskId);
/**
* 關閉定時任務
* @param taskId
* @return
*/
public Task shutdownTask(String taskId);
/**
* 啟動所有定時任務
*
*/
public void startAllTask();
/**
* 關閉所有定時任務
*/
public void shutdownAllTask();
}實現(xiàn)方法:
package com.kevin.schedule.service.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import com.gupaoedu.schedule.entity.Task;
import com.gupaoedu.schedule.proxy.TriggerProxy;
import com.gupaoedu.schedule.service.IScheduleService;
/**
* 任務管理,負責輸出和管理日志
* @author Tom
*/
@Service
public class ScheduleService implements IScheduleService,ApplicationContextAware{
//自己不能再重新搞一個工廠出來,必須和Spring引用的調度工廠是同一個
//才能實現(xiàn)無縫集成,而且可以動態(tài)修改配置文件已經配置好的任務
@Autowired private SchedulerFactoryBean schedulerFactory;
//保存所有任務表達式的描述
private Map<String,String> cronDesc = new LinkedHashMap<String,String>();
//所有的任務都放到任務池中
private static Map<String,Task> taskPool = new LinkedHashMap<String,Task>();
private static ApplicationContext app;
public ScheduleService(){}
/**
* 創(chuàng)建一個調度任務
* @param m
* @return
* @throws Exception
*/
private Task createTask(Method m) throws Exception{
//任務ID就是時間戳,再加上一個隨機數
Task task = new Task("" + System.currentTimeMillis());
//通過方法,可以獲取到方法所在的class
Class clazz = m.getDeclaringClass();
//設置任務組
task.setGroup(clazz.getName());
//設置觸發(fā)器信息
//一個觸發(fā)器對應一個方法,在此處,所謂的觸發(fā)器還只是一個概念
//概念是一個字符描述,它記錄了觸發(fā)的目標信息
task.setTrigger(clazz.getName() + "." + m.getName());
Annotation sc = m.getAnnotation(Scheduled.class);
Method cronM = sc.getClass().getMethod("cron",null);
String cron = cronM.invoke(sc, null).toString();
task.setTriggerDesc(cronDesc.get(cron));
task.setCron(cron);
task.setCronDesc(cronDesc.get(cron));
//此時,就已經完成一個task的封裝
return task;
}
@Override
public void setApplicationContext(ApplicationContext app) throws BeansException {
this.app = app;
//加載所有任務到任務隊列
for(String name : app.getBeanDefinitionNames()){
try{
Class<?> c = app.getBean(name).getClass();
for(Method m : c.getMethods()){
//只要是添加了Scheduled注解的都給他添加到調度工廠中
if(m.isAnnotationPresent(Scheduled.class)){
Task task = createTask(m);
//將任務加入隊列準備啟動
createTask(task);
}
}
}catch(Exception e){
continue;
}
}
}
public Task getTask(String taskId) {
return taskPool.get(taskId);
}
@Override
public List<Task> getAllTask() {
if(taskPool.size() == 0){
return new ArrayList<Task>();
}
List<Task> r = new ArrayList<Task>();
r.addAll(taskPool.values());
return r;
}
private Task createTask(Task task) throws Exception{
if(null == task.getGroup() || task.getGroup().trim().length() == 0){ return null; }
//還是拿到字節(jié)碼
Class<?> clazz = Class.forName(task.getGroup());
//先從容器中獲取
Object target = null;
try{
//從Spring容器中提取已經創(chuàng)建好的對象引用
target = app.getBean(clazz);
}catch(Exception e){
}
//如果Spring容器沒有幫我們創(chuàng)建,那么就自己創(chuàng)建實例
if(target == null){
target = clazz.newInstance();
}
//把觸發(fā)器需要調用的方法找出來,還是用反射
Method m = clazz.getMethod(task.getTrigger().replaceAll(task.getGroup() + ".", ""));
//把任務ID取出來,時間戳
String taskId = task.getId();
//================ 事前準備 ====================
//拿到Quartz中的調度器
Scheduler sched = schedulerFactory.getScheduler();
//創(chuàng)建一個Detail
JobDetail taskDetail = new JobDetail(taskId, task.getGroup(), TriggerProxy.class);// 任務名,任務組,任務執(zhí)行類
// 觸發(fā)器
CronTrigger trigger = new CronTrigger(taskId, task.getTrigger());// 觸發(fā)器名,觸發(fā)器組
//在這里設置CronExpression表達式
trigger.setCronExpression(task.getCron());// 觸發(fā)器時間設定
//JobDataMap 用來存儲附加信息
//利用這么一個API,把自定義的信息添加到Map中
trigger.getJobDataMap().put(TriggerProxy.DATA_TARGET_KEY, target);
trigger.getJobDataMap().put(TriggerProxy.DATA_TRIGGER_KEY, m);
// m.getParameterTypes()
trigger.getJobDataMap().put(TriggerProxy.DATA_TRIGGER_PARAMS_KEY, new Object[]{});
trigger.getJobDataMap().put(TriggerProxy.DATA_TASK_KEY, task);
sched.scheduleJob(taskDetail, trigger);
// 如果這個任務沒有被主動關閉,我們就給他啟動
if (!sched.isShutdown()) {
sched.start();
}
//放入我們的任務池
if(!taskPool.containsKey(taskId)){
taskPool.put(taskId, task);
}
return task;
}
/**
* 添加一個定時任務,使用默認的任務組名,觸發(fā)器名,觸發(fā)器組名
*/
public Task createTask(String taskName,String taskClassName,String triggerName,String cron) throws Exception{
return createTask(taskName,null,taskClassName,null,triggerName,cron);
}
/**
* 添加一個定時任務
*/
private Task createTask(String taskName, String taskGroupName, String taskClassName,String triggerGroupName, String triggerName,String cron) throws Exception{
//根據類名,利用反射機制獲取到類的字節(jié)碼
//約定優(yōu)于配置
Class<?> clazz = Class.forName(taskClassName); //就是類名全程,包名.類名
Method m = clazz.getMethod(triggerName); //顯然就是方法名
Task task = createTask(m);
task.setName(taskName);
if(null != taskGroupName){
task.setGroup(taskGroupName);
}
task.setCron(cron);
return createTask(task);
}
/**
* 修改一個任務的觸發(fā)時間(使用默認的任務組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task modifyTaskCron(String taskId, String cron) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(taskId,task.getTrigger());
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
JobDetail taskDetail = sched.getJobDetail(taskId,task.getGroup());
Class objJobClass = taskDetail.getJobClass();
removeTask(taskId);
//重新生成ID
task.setId("" + System.currentTimeMillis());
task.setCron(cron);
createTask(task);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 移除一個任務(使用默認的任務組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task removeTask(String taskId) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(taskId, task.getTrigger());// 停止觸發(fā)器
sched.unscheduleJob(taskId, task.getGroup());// 移除觸發(fā)器
sched.deleteJob(taskId, task.getGroup());// 刪除任務
taskPool.remove(taskId);
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 暫停任務
* @param taskId
*/
public Task pauseTask(String taskId){
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(task.getId(), task.getTrigger());// 停止觸發(fā)器
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
*
*
*/
public Task restartTask(String taskId) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
// 重啟觸發(fā)器
sched.resumeTrigger(task.getId(),task.getTrigger());
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 關閉任務
* @param taskId
*/
public Task shutdownTask(String taskId){
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(taskId, task.getTrigger());// 停止觸發(fā)器
sched.unscheduleJob(taskId, task.getGroup());// 移除觸發(fā)器
sched.deleteJob(taskId, task.getGroup());// 刪除任務
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 啟動所有定時任務
*
*/
public void startAllTask() {
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 關閉所有定時任務
*/
public void shutdownAllTask() {
try {
Scheduler sched = schedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}到此這篇關于Spring+Quartz實現(xiàn)動態(tài)任務調度詳解的文章就介紹到這了,更多相關Spring+Quartz動態(tài)任務內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java object wait notify notifyAll代碼解析
這篇文章主要介紹了Java object wait notify notifyAll代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11
Java序列化問題:“Serialized class has not impl
在Java開發(fā)中,序列化(Serialization)是一個常見的操作,尤其是在分布式系統(tǒng)、網絡通信或數據持久化場景中,然而,序列化過程中可能會遇到各種問題,其中最常見的一個錯誤是Serialized class has not implement Serializable interface,本文給大家介紹了相關的解決方法2025-02-02
Android?Studio中創(chuàng)建java工程的完整步驟
Android?Studio創(chuàng)建java工程是非常麻煩的,因為Android?Studio沒有提供直接創(chuàng)建java工程的方法,下面這篇文章主要給大家介紹了關于Android?Studio中創(chuàng)建java工程的完整步驟,需要的朋友可以參考下2024-01-01
Javaweb開發(fā)中通過Servlet生成驗證碼圖片
這篇文章主要為大家詳細介紹了Javaweb開發(fā)中通過Servlet生成驗證碼圖片的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05
SpringBoot如何使用Template請求http接口
在Spring?Boot中,如果你想要通過模板(template)的方式連接HTTP服務,并發(fā)送HTTP請求,有幾種不同的方式可以實現(xiàn),但最直接和常用的方式之一是使用RestTemplate,這篇文章主要介紹了SpringBoot使用Template請求http接口,需要的朋友可以參考下2024-08-08
SpringSecurityOAuth2 如何自定義token信息
這篇文章主要介紹了SpringSecurityOAuth2 自定義token信息的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06

