Quartz定時器可視化管理功能的簡單實現


眾所周知,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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM