眾所周知,Quartz框架是一款強大的定時器框架,下面簡單地介紹下Quartz定時器的可視化管理功能實現,這樣就能通過后台來動態地管理定時器啦!
相關表的導入
首先去Quartz官網下載相關Jar包,點擊這里下載,當前最新版本為2.2.3,本示例也是基於2.2.3版本進行開發。
解壓完成后,進入quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables目錄下,因為用的是Mysql數據庫,所以用編輯器(比如Notepad++)打開tables_mysql.sql文件。
# # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar # # PLEASE consider using mysql with innodb tables to avoid locking issues # # In your Quartz properties file, you'll need to set # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;
將上面的Sqly語句復制粘貼執行,成功之后數據庫一共有11張表。
幾個重要表的功能,簡單說明如下(資料參考這里):
1、QRTZ_JOB_DETAILS:存儲的是job的詳細信息,包括:[DESCRIPTION]描述,[IS_DURABLE]是否持久化,[JOB_DATA]持久化對象等基本信息。 2、QRTZ_TRIGGERS:觸發器信息,包含:job的名,組外鍵,[DESCRIPTION]觸發器的描述等基本信息,還有[START_TIME]開始執行時間,[END_TIME]結束執行時間,[PREV_FIRE_TIME]上次執行時間,[NEXT_FIRE_TIME]下次執行時間,[TRIGGER_TYPE]觸發器類型:simple和cron,[TRIGGER_STATE]執行狀態:WAITING,PAUSED,ACQUIRED分別為:等待,暫停,運行中。 3、QRTZ_CRON_TRIGGERS:保存cron表達式。 4、QRTZ_SCHEDULER_STATE:存儲集群中note實例信息,quartz會定時讀取該表的信息判斷集群中每個實例的當前狀態,INSTANCE_NAME:之前配置文件中org.quartz.scheduler.instanceId配置的名字,就會寫入該字段,如果設置為AUTO,quartz會根據物理機名和當前時間產生一個名字。[LAST_CHECKIN_TIME]上次檢查時間,[CHECKIN_INTERVAL]檢查間隔時間。 5、QRTZ_PAUSED_TRIGGER_GRPS:暫停的任務組信息。 6、QRTZ_LOCKS,悲觀鎖發生的記錄信息。 7、QRTZ_FIRED_TRIGGERS,正在運行的觸發器信息。 8、QRTZ_SIMPLE_TRIGGERS,簡單的出發器詳細信息。 9、QRTZ_BLOB_TRIGGERS,觸發器存為二進制大對象類型(用於Quartz用戶自己觸發數據庫定制自己的觸發器,然而JobStore不明白怎么存放實例的時候)。
設置配置文件
首先定義名為 quartz.properties 的屬性文件,內容如下
org.quartz.scheduler.instanceName= DefaultQuartzScheduler org.quartz.scheduler.rmi.export= false org.quartz.scheduler.rmi.proxy= false org.quartz.scheduler.wrapJobExecutionInUserTransaction= false #定義線程池的數量 org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=10 org.quartz.threadPool.threadPriority=5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true org.quartz.jobStore.misfireThreshold=60000 #內存存儲配置 #org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore #持久化配置 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate #數據庫表前綴 org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = qzDS #JDBC驅動 org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driver #數據庫連接信息 org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/demo org.quartz.dataSource.qzDS.user= root org.quartz.dataSource.qzDS.password= root org.quartz.dataSource.qzDS.maxConnections = 30
定義 applicationContext-quartz.xml 配置文件,
其中: classpath:config/quartz.properties指定quartz.properties的位置,
內容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd" default-lazy-init="false"> <description>Quartz Configuration</description> <bean id="jobFactory" class="com.dgd.quartz.factory.JobFactory"></bean> <bean id="quartzScheduler" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="configLocation" value="classpath:config/quartz.properties" /> <property name="jobFactory" ref="jobFactory"></property> <property name="applicationContextSchedulerContextKey" value="applicationContextKey"></property> </bean> </beans>
id=”jobFactory” 的Java類定義如下:
package com.dgd.quartz.factory; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SpringBeanJobFactory; /** * @Author DGD * @date 2017/10/17. */ public class JobFactory extends SpringBeanJobFactory implements ApplicationContextAware{ private ApplicationContext applicationContext; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object jobInstance = super.createJobInstance(bundle); applicationContext.getAutowireCapableBeanFactory().autowireBean(jobInstance); return jobInstance; } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.applicationContext = context; } }
至於JobFactory 類的作用,就是為了讓自定義的Job能夠自動注入Spring容器托管的對象,具體細節可參考Quartz與Spring集成 Job如何自動注入Spring容器托管的對象 。
在 applicationContext.xml 引入配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd" default-autowire="byType"> <!--啟動spring MVC 注解--> <context:annotation-config/> <context:component-scan base-package="com.dgd"/> <!--引入quartz.properties配置文件--> <context:property-placeholder location="classpath:config/quartz.properties" ignore-unresolvable="true"/> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
在web.xml中配置 applicationContext-quartz.xml,如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring/applicationContext.xml, classpath:spring/applicationContext-quartz.xml </param-value> </context-param> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
配置文件已經搞定,接下來講講創建定時任務和刪除定時任務的核心方法。
首先定義一個管理定時任務的表,SQL語句如下:
CREATE TABLE `t_timer_manage` ( `timer_id` int(10) NOT NULL AUTO_INCREMENT COMMENT '定時任務ID', `timer_job_name` varchar(60) NOT NULL COMMENT '定時器名稱', `timer_trigger_name` varchar(60) NOT NULL COMMENT '觸發器名稱', `timer_cron_expression` varchar(60) NOT NULL COMMENT 'cron表達式', `timer_target_object` varchar(100) NOT NULL COMMENT '目標類名(全路徑)', `timer_method_name` varchar(60) DEFAULT NULL COMMENT '類名對應的方法名', `timer_start_time` datetime NOT NULL COMMENT '開始執行時間', `timer_end_time` datetime NOT NULL COMMENT '結束執行時間', `timer_status` int(2) NOT NULL COMMENT '定時器運行狀態(1、運行中 ,2、待運行,3、運行失敗,4、失效)', `timer_description` varchar(200) NOT NULL COMMENT '定時器詳細描述', `create_user_id` int(10) NOT NULL COMMENT '創建人', `create_time` datetime NOT NULL COMMENT '創建時間', `modify_user_id` int(10) NOT NULL COMMENT '修改人', `modify_time` datetime NOT NULL COMMENT '修改時間', `extend1` varchar(200) DEFAULT NULL COMMENT '備用字段', `extend2` varchar(200) DEFAULT NULL COMMENT '備用字段', `extend3` varchar(200) DEFAULT NULL COMMENT '備用字段', `extend4` varchar(200) DEFAULT NULL COMMENT '備用字段', `extend5` varchar(200) DEFAULT NULL COMMENT '備用字段', PRIMARY KEY (`timer_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='定時器管理表';
生成該表的POJO類,比如命名為:TTimerManage,定義如下(setter方法和getter方法已省略):
package com.dgd.entity; import java.io.Serializable; import java.util.Date; public class TTimerManage implements Serializable { private Integer timerId; private String timerJobName; private String timerTriggerName; private String timerCronExpression; private String timerTargetObject; private String timerMethodName; private Date timerStartTime; private Date timerEndTime; private Integer timerStatus; private String timerDescription; private Integer createUserId; private Date createTime; private Integer modifyUserId; private Date modifyTime; private String extend1; private String extend2; private String extend3; private String extend4; private String extend5; private static final long serialVersionUID = 1L; (setter方法和getter方法已省略)... }
定義一個定時器管理接口:
package com.dgd.service; import com.dgd.common.exception.TaskException; import com.dgd.entity.TTimerManage; import java.io.Serializable; import java.util.List; /** * @Author DGD * @date 2017/10/17. */ public interface QuartzManagerService extends Serializable{ /** * 創建定時任務並執行 * @param timerManage * @return */ boolean createTimerTask(TTimerManage timerManage) throws TaskException; /** * 停止執行定時任務 * @param timerManage * @return */ boolean removeTimerTask(TTimerManage timerManage) throws TaskException; }
TaskException為自定義異常,定義如下:
package com.dgd.common.exception; /** * @Author DGD * @date 2017/10/17. */ public class TaskException extends RuntimeException{ private int code=-1; private Object data; public TaskException(String msg){ super(msg); } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public TaskException(String msg, int code){ super(msg); } public int getCode() { return code; } public void setCode(int code) { this.code = code; } }
接下來實現QuartzManagerService 接口,如下:
package com.dgd.service; import com.dgd.common.exception.TaskException; import com.dgd.entity.TTimerManage; import com.dgd.entity.TTimerManageExample; import com.dgd.persistence.dao.TTimerManageMapper; import org.quartz.*; import com.dgd.quartz.listener.TaskJobListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * @Author DGD * @date 2017/10/17. */ @Service public class QuartzManagerServiceImpl implements QuartzManagerService { private Logger log = LoggerFactory.getLogger(QuartzManagerServiceImpl.class); @Resource(name = "quartzScheduler") private Scheduler scheduler; //示例代碼采用的ORM框架為Mybatis @Autowired private TTimerManageMapper timerManageMapper; @Override public boolean createTimerTask(TTimerManage timerManage) throws TaskException { if (timerManage == null) { throw new TaskException("定時任務不能為空"); } AtomicBoolean result = new AtomicBoolean(false); try { Trigger trigger = scheduler.getTrigger(new TriggerKey(timerManage.getTimerTriggerName(),timerManage.getTimerTriggerName())); if (trigger != null) { //已存在觸發器,即計划任務已存在 if (scheduler.checkExists(trigger.getJobKey()) && scheduler.checkExists(trigger.getKey())) { //修改任務狀態:Constants.TimerStatus.運行中=1 timerManage.setTimerStatus(1); timerManage.setModifyTime(new Date()); timerManageMapper.updateByPrimaryKey(timerManage); //記錄日志略 result.set(true); } }else{ // 1、創建一個JobDetail實例 JobDetail jobDetail = null; Object object = Class.forName(timerManage.getTimerTargetObject()).newInstance(); if (object instanceof Job) { Job job = (Job) object; jobDetail = JobBuilder.newJob(job.getClass()) //任務執行類 .withIdentity(timerManage.getTimerJobName(),timerManage.getTimerJobName()) //任務名,任務組 .build(); } if (jobDetail != null) { //設置表達式 CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule(timerManage.getTimerCronExpression()); //創建Trigger trigger = TriggerBuilder.newTrigger() //觸發器名,觸發器組名 .withIdentity(timerManage.getTimerTriggerName(),timerManage.getTimerTriggerName()) .startNow() .withSchedule(builder) .build(); //將該任務添加到監聽器中 scheduler.getListenerManager().addJobListener(new TaskJobListener(), KeyMatcher.keyEquals(jobDetail.getKey())); //調度執行 scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); //改變任務狀態 if (scheduler.checkExists(jobDetail.getKey()) && scheduler.checkExists(trigger.getKey())) { //修改任務狀態:Constants.TimerStatus.運行中=1 timerManage.setTimerStatus(1); //進行中 timerManage.setModifyTime(new Date()); timerManageMapper.updateByPrimaryKey(timerManage); //記錄日志略 result.set(true); } } } } catch (Exception e) { timerManage.setTimerStatus(3); //運行失敗 timerManage.setModifyTime(new Date()); timerManage.setModifyUserId(-1); timerManageMapper.updateByPrimaryKey(timerManage); //記錄日志 } return result.get(); } @Override public boolean removeTimerTask(TTimerManage timerManage) throws TaskException { if (timerManage == null) { throw new TaskException("定時任務不能為空"); } AtomicBoolean result = new AtomicBoolean(false); try { Trigger trigger = scheduler.getTrigger( new TriggerKey(timerManage.getTimerTriggerName(),timerManage.getTimerTriggerName())); if (trigger != null) { //定時任務在執行 if (scheduler.checkExists(trigger.getKey()) && scheduler.checkExists(trigger.getJobKey())) { //如果定時任務狀態已失效,則移除,Constants.TimerStatus.失效=4 if(scheduler.deleteJob(trigger.getJobKey())){ if(timerManage.getTimerStatus().intValue() != Constants.TimerStatus.失效){ timerManage.setTimerStatus(Constants.TimerStatus.失效); timerManage.setModifyTime(new Date()); timerManageMapper.updateByPrimaryKey(timerManage); } //記錄日志略 }else{ log.info("定時任務:"+timerManage.getTimerJobName()+"停止執行失敗"); } }else{ if(timerManage.getTimerStatus().intValue() != Constants.TimerStatus.失效){ timerManage.setTimerStatus(Constants.TimerStatus.失效); timerManage.setModifyTime(new Date()); timerManageMapper.updateByPrimaryKey(timerManage); } //記錄日志略 } }else{ if(timerManage.getTimerStatus().intValue() != Constants.TimerStatus.失效){ timerManage.setTimerStatus(Constants.TimerStatus.失效); timerManage.setModifyTime(new Date()); timerManageMapper.updateByPrimaryKey(timerManage); } //記錄日志略 } } catch (Exception e) { log.info("定時任務:"+timerManage.getTimerJobName() +",執行停止運行操作出現異常,異常信息:"+e.getMessage()); } return result.get(); } }
上面的代碼中,TaskJobListener 為自定義的監聽器類,用來判斷執行的定時任務執行時間是否有效,如果無效則將其剔除,定義如下:
package com.dgd.quartz.listener; import com.dgd.common.SpringContextHolder; import com.dgd.entity.TTimerManage; import com.dgd.entity.TTimerManageExample; import com.dgd.persistence.dao.TTimerManageMapper; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Date; import java.util.List; /** * @Author DGD * @date 2017/10/18. */ public class TaskJobListener implements JobListener{ private static final String NAME = "TaskJobListener"; private static final Logger log = LoggerFactory.getLogger(TaskJobListener.class); @Override public String getName() { return NAME; } //定時任務執行之前需要做的事 @Override public void jobToBeExecuted(JobExecutionContext jobExecutionContext) { Scheduler scheduler = jobExecutionContext.getScheduler(); //獲取Spring容器托管的Bean TTimerManageMapper timerManageMapper = SpringContextHolder.getBean(TTimerManageMapper.class); //拿到定時任務名字和觸發器名字 JobKey jobKey = jobExecutionContext.getJobDetail().getKey(); TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey(); String jobName = jobKey.getName(); String triggerName = triggerKey.getName(); log.info("檢查定時任務執行時間是否有效,當前的定時任務為:"+jobName); //根據名字去查詢定時任務記錄 TTimerManageExample example = new TTimerManageExample(); example.createCriteria().andTimerJobNameEqualTo(jobName).andTimerTriggerNameEqualTo(triggerName); List<TTimerManage> list = timerManageMapper.selectByExample(example); if (list != null && list.size() > 0) { for (TTimerManage timerManage : list) { if (timerManage != null) { //判斷定時任務執行時間是否有效,無效則將任務移除 if (Calendar.getInstance().getTime().compareTo(timerManage.getTimerEndTime())>0 || Calendar.getInstance().getTime().compareTo(timerManage.getTimerStartTime())<0) { try { if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) { //成功移除任務 if (scheduler.deleteJob(jobKey)) { //修改定時任務狀態 timerManage.setTimerStatus(4); timerManage.setModifyTime(new Date()); timerManage.setModifyUserId(-1); timerManageMapper.updateByPrimaryKey(timerManage); //記錄日志略 } } } catch (Exception e) { log.error("檢查定時任務執行時間是否有效過程出現異常,異常信息="+ e.getMessage()); } } } } } } @Override public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) { } @Override public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) { } }
工具類SpringContextHolder定義如下:
package com.dgd.common; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * @Author DGD * @date 2017/10/18. */ public class SpringContextHolder implements ApplicationContextAware, DisposableBean { private static ApplicationContext applicationContext = null; private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class); /** * 取得存儲在靜態變量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { assertContextInjected(); return applicationContext; } /** * 從靜態變量applicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ public static <T> T getBean(String name) { assertContextInjected(); return (T) applicationContext.getBean(name); } /** * 從靜態變量applicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ public static <T> T getBean(Class<T> requiredType) { assertContextInjected(); return applicationContext.getBean(requiredType); } /** * 清除SpringContextHolder中的ApplicationContext為Null. */ public static void clearHolder() { if (logger.isDebugEnabled()){ logger.debug("clear ApplicationContext:" + applicationContext); } applicationContext = null; } /** * 實現ApplicationContextAware接口, 注入Context到靜態變量中. */ @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringContextHolder.applicationContext = applicationContext; } /** * 實現DisposableBean接口, 在Context關閉時清理靜態變量. */ @Override public void destroy() throws Exception { SpringContextHolder.clearHolder(); } /** * 檢查ApplicationContext不為空. */ private static void assertContextInjected() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext is not be injected,please config SpringContextHolder in applicationContext.xml"); } } }
記得要在 applicationContext.xml 注冊:
<bean class="com.dgd.common.SpringContextHolder"/>
測試任務類如下:
package com.dgd.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; import java.util.Date; /** * @Author DGD * @date 2017/10/17. */ public class HelloWorldJob implements Job { Logger log = LoggerFactory.getLogger(HelloWorldJob.class); @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { +new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"-------------------"); log.info("--------------- Hello World! ---------------"); try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("---------------結束執行HelloWorldJOb測試任務:" +new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"-------------------"); } }
數據庫執行以下語句:
INSERT INTO `t_timer_manage` ( `timer_job_name`, `timer_trigger_name`, `timer_cron_expression`, `timer_target_object`, `timer_method_name`, `timer_start_time`, `timer_end_time`, `timer_status`, `timer_description`, `create_user_id`, `create_time`, `modify_user_id`, `modify_time`, `extend1`, `extend2`, `extend3`, `extend4`, `extend5` ) VALUES ( 'HelloWorldJob', 'HelloWorldTrigger', '0/10 * * * * ?', 'com.dgd.quartz.job.HelloWorldJob', NULL, '2017-08-02 10:23:00', '2017-10-21 11:43:03', '2', '測試定時器', -1, NOW(), -1, NOW(), NULL, NULL, NULL, NULL, NULL ); INSERT INTO `t_timer_manage` ( `timer_job_name`, `timer_trigger_name`, `timer_cron_expression`, `timer_target_object`, `timer_method_name`, `timer_start_time`, `timer_end_time`, `timer_status`, `timer_description`, `create_user_id`, `create_time`, `modify_user_id`, `modify_time`, `extend1`, `extend2`, `extend3`, `extend4`, `extend5` ) VALUES ( 'HelloWorldJob', 'HelloWorldTrigger', '0/10 * * * * ?', 'com.dgd.quartz.job.HelloWorldJob', NULL, '2017-08-02 10:23:00', '2017-10-21 11:43:03', '2', '測試定時器', -1, NOW(), -1, NOW(), NULL, NULL, NULL, NULL, NULL );
現在,只要在Controller層添加相關調用接口的代碼,(后台的增刪改查代碼和頁面默認已有),即完成對定時任務的可視化管理。
---------------------
作者:書隱辭
來源:CSDN
原文:https://blog.csdn.net/u012075383/article/details/78270941
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!