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]触发器类型:simplecron[TRIGGER_STATE]执行状态:WAITINGPAUSEDACQUIRED分别为:等待,暂停,运行中。

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
    );

现在,只要在Controller层添加相关调用接口的代码,(后台的增删改查代码和页面默认已有),即完成对定时任务的可视化管理。

等有时间再把完整项目的github地址发布在这里。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值