ssm项目中动态Quartz定时任务的实现(定时任务存在表中,而不是在.xml中)

时间:2021-01-22 07:46:13

大家好,小学生又来为初级者分享一下技术了。这节我们来分享quartz动态定时任务的实现,因为楼主公司项目的需要,所以前端时间自己了解了一下动态更改的quartz定时任务功能。我刚开始也是看了需要帖子,发现大多数定时任务还是写在.xml配置里面的,这样写的最大缺点就是如果因为公司需要把定时任务执行的时间、或者是执行类更换,就需要修改.xml代码并重新提交发布版本才行。为此出了一种写到数据库里的动态定时任务技术。如下......

我在上节讲了maven创建ssm框架的多模块项目,此次也是根据上次的代码进行编写分享的,如有需要可以点击查看上篇博客 maven多模块

1、我们需要在父项目的pom.xml文件中加入jar依赖:

<dependency><span style="white-space:pre">	
<span style="white-space:pre">	</span><groupId>org.quartz-scheduler</groupId>	<artifactId>quartz</artifactId>	<version>1.8.6</version></dependency><dependency>	<groupId>org.slf4j</groupId>	<artifactId>slf4j-log4j12</artifactId>	<version>1.7.2</version></dependency>

 

2、为了项目开发方便的需要,我们需要创建两个工具实体类:

(1)定时任务工具类QuartzManager(主要是对quartz的新增、更改、关闭等)

package com.xzhang.util;

import java.util.Date;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

/**
* @Title: QuartzManager.java
* @Package com.xzhang.util
* @Description: TODO(定时任务管理类)
* @author zx
* @date 2016-8-22 下午3:28:57
*/
public class QuartzManager {

private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME";
private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME";

/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param jobName
* 任务名
* @param cls
* 任务
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:47:44
* @version V2.0
*/
@SuppressWarnings("unchecked")
public static void addJob(String jobName, Class cls, String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);// 任务名,任务组,任务执行类
// 触发器
CronTrigger trigger = new CronTrigger(jobName, TRIGGER_GROUP_NAME);// 触发器名,触发器组
trigger.setCronExpression(time);// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description: 添加一个定时任务
*
* @param jobName
* 任务名
* @param jobGroupName
* 任务组名
* @param triggerName
* 触发器名
* @param triggerGroupName
* 触发器组名
* @param jobClass
* 任务
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:48:15
* @version V2.0
*/
@SuppressWarnings("unchecked")
public static void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = new JobDetail(jobName, jobGroupName, jobClass);// 任务名,任务组,任务执行类
// 触发器
CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组
trigger.setCronExpression(time);// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
*
* @param jobName
* @param time
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:49:21
* @version V2.0
*/
@SuppressWarnings("unchecked")
public static void modifyJobTime(String jobName, String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(jobName,TRIGGER_GROUP_NAME);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
JobDetail jobDetail = sched.getJobDetail(jobName,JOB_GROUP_NAME);
Class objJobClass = jobDetail.getJobClass();
removeJob(jobName);
addJob(jobName, objJobClass, time);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description: 修改一个任务的触发时间
*
* @param triggerName
* @param triggerGroupName
* @param time
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:49:37
* @version V2.0
*/
public static void modifyJobTime(String triggerName,
String triggerGroupName, String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerName,triggerGroupName);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
CronTrigger ct = (CronTrigger) trigger;
// 修改时间
ct.setCronExpression(time);
// 重启触发器
sched.resumeTrigger(triggerName, triggerGroupName);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* 更新任务时间:先移除、再新增
* @param triggerName
* @param triggerGroupName
* @param time
*/
public static void modifyJobTimeTrue(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time){
//先移除
removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
//再新增
addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, time);
}

/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*
* @param jobName
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:49:51
* @version V2.0
*/
public static void removeJob(String jobName) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(jobName, TRIGGER_GROUP_NAME);// 停止触发器
sched.unscheduleJob(jobName, TRIGGER_GROUP_NAME);// 移除触发器
sched.deleteJob(jobName, JOB_GROUP_NAME);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description: 移除一个任务
*
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:50:01
* @version V2.0
*/
public static void removeJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器
sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器
sched.deleteJob(jobName, jobGroupName);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description:启动所有定时任务
*
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:50:18
* @version V2.0
*/
public static void startJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* @Description:关闭所有定时任务
*
*
* @Title: QuartzManager.java
* @Copyright: Copyright (c) 2014
*
* @author Comsys-LZP
* @date 2014-6-26 下午03:50:26
* @version V2.0
*/
public static void shutdownJobs() {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* 停止一个job任务
* @param jobkey
* @throws SchedulerException
*/
public static void pauseJob(String jobName, String groupName,String triggerName) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
//sched.pauseTrigger(triggerName, groupName);
//sched.pauseJob(jobName, groupName);
sched.interrupt(jobName, groupName);
}

/**
* 恢复相关的job任务
* @param jobkey
* @throws SchedulerException
*/
public static void resumeJob(String jobName, String groupName,String triggerName) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
//sched.resumeTrigger(triggerName, groupName);
sched.resumeJob(jobName, groupName);
}

}
(2)SpringContextUtil(spring相关的工具类,主要为了获取到项目中定义的service bean)

/**
*
*/
package com.xzhang.web;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

/**
* Spring相关工具类
*
* @author Jian
* @date 2012-8-15
*/
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;

/**
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
*/
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}

/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}

/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}

/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return (T) applicationContext.getBeansOfType(clazz);
}

private static void checkApplicationContext() {
if (applicationContext == null)
throw new IllegalStateException("applicaitonContext未注入,请在spring-mvc.xml中定义SpringContextUtil");
}

public static String getMessage(String key, HttpServletRequest request) {
return applicationContext.getMessage(key, null, getLocal(request));
}

public static String getMessage(String key, String[] args, HttpServletRequest request) {
return applicationContext.getMessage(key, args, getLocal(request));
}

public static Locale getLocal(HttpServletRequest request) {
Locale locale = (Locale) request.getSession().getAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale == null) {
locale = request.getLocale();
}

return locale;
}
}


3、创建一张定时任务详细表(即用来存储的所有的定时任务信息),楼主这里用的是MySQL

-- 定时任务表
CREATE TABLE t_instmt_quartz
(
id VARCHAR(100) PRIMARY KEY ,
job_group VARCHAR(256) COMMENT '任务组',
trigger_group VARCHAR(256) COMMENT '触发器组',
job_name VARCHAR(128) COMMENT '任务名',
trigger_name VARCHAR(128) COMMENT '触发器名',
class_name VARCHAR(128) COMMENT '执行代码的类名',
enable_status VARCHAR(2) DEFAULT '1' COMMENT '是否禁用:0禁用;1启用',
trigger_cron VARCHAR(128) COMMENT '触发器类型(时间) */5 * * * * ?',
trigger_status VARCHAR(2) COMMENT '任务状态:0关闭;1运行中;2暂停;',
crate_time DATETIME COMMENT '创建时间',
update_time DATETIME COMMENT '更新时间',
desc_ript VARCHAR(1024) COMMENT '描述'
);

手动输入一条数据如下图

ssm项目中动态Quartz定时任务的实现(定时任务存在表中,而不是在.xml中)


4、然后根据表创建实体类:QuartzManager;创建相关的dao层、service层

5、创建一个总的定时任务启动类:InstantiationTracingBeanPostProcessor,用来在spring加载完成就进行启动检索所有的定时任务并启动。

package com.xzhang.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import com.alibaba.fastjson.JSON;
import com.xzhang.model.InstmtQuartz;
import com.xzhang.service.IQuartzService;
import com.xzhang.util.QuartzManager;

/**
* @Title: InstantiationTracingBeanPostProcessor.java
* @Package com.xzhang.controller
* @Description: TODO(所有定时任务的启动类:当spring启动的时候它就跟着启动)
* @author zx
* @date 2016-8-22 下午4:28:24
*/
public class InstantiationTracingBeanPostProcessor {

private IQuartzService quartzService;
public IQuartzService getQuartzService() {
return quartzService;
}
public void setQuartzService(IQuartzService quartzService) {
this.quartzService = quartzService;
}


/* spring加载完就执行该方法:init-method="autoLoadTask" */
public void autoLoadTask() {
//获取到所有需要启动的quartz集合
System.out.println("【系统启动】所有定时任务开启...");
Map<String, Object> conditions = new HashMap<String, Object>();
conditions.put("enablestatus", InstmtQuartz.ENABLE_STATUS_YES);
conditions.put("triggerstatus", "");
List<InstmtQuartz> quartzList = quartzService.getInstmtQuartzList(conditions);
if(null == quartzList)return;
for(int i=0;i<quartzList.size();i++){
System.out.println("定时任务个数:"+quartzList.size());
try {
QuartzManager.addJob(quartzList.get(i).getJobname(), quartzList.get(i).getJobgroup(),
quartzList.get(i).getTriggername(), quartzList.get(i).getTriggergroup(),
Class.forName(quartzList.get(i).getClassname()), quartzList.get(i).getTriggercron());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

}

}


6、同时我们需要在spring-mvc.xml文件中添加如下配置:

<!-- 定义SpringContextUtil工具类 -->
<bean id="springContextUtil" class="com.xzhang.web.SpringContextUtil" />
<!-- 定时任务:要求所有的定时任务在spring启动之后跟着启动 --><bean id="instantiationTracingBeanPostProcessor" init-method="autoLoadTask" class="com.xzhang.controller.InstantiationTracingBeanPostProcessor" >	<property name="quartzService" ref="quartzService" /></bean>

如上代码所示:(1)第一个配置是为了定义声明SpringContextUtil,因为利用了这个工具类去获取了项目中注解的service;

(2)第二个配置是为了在spring启动完成后就执行InstantiationTracingBeanPostProcessor类中的方法autoLoadTask,并注入了quartzService


7、编写一个测试的定时任务执行类,如下:

package com.xzhang.controller;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.UnableToInterruptJobException;

import com.xzhang.model.User;
import com.xzhang.service.IUserService;
import com.xzhang.web.SpringContextUtil;

/**
* 定时任务--打印测试定时任务是否成功
* @author zx
*
*/
public class TestQuartzTask implements InterruptableJob {
protected Logger log = Logger.getLogger(this.getClass());
private boolean _interrupted = false;

/*此处引入相关的service*/
private IUserService userService = SpringContextUtil.getBean("userService");


@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
if(null == userService){
System.out.println("userService为null");
}
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sf.format(new Date())+
"定时任务TestQuartzTask.class启动!!!");
/*
* 获取所有的用户列表,并循环打印
* */
List<User> users = userService.getAll();
if(null != users && users.size()>0){
for(User u : users){
//判断是否点击了‘立刻停止’
if(_interrupted) {
System.out.println("定时任务ExpectedBillSmsTask.class立刻停止!!!");
return;
}

System.out.println(sf.format(new Date())+":"+u.getName());
}
}
}

@Override
public void interrupt() throws UnableToInterruptJobException {
System.out.println("【关闭】interrupt执行立刻停止:test定时发送...");
log.info("【关闭】interrupt执行立刻停止:test定时发送...");
_interrupted = true;
}

}
注:这个执行类实现了InterruptableJob类,之所以实现这个类主要是想利用InterruptableJob类中private boolean _interrupted = false;这个作用是当在定时任务管理界面点击【立即停止】时,会让_interrupted =true,会中止for循环,也就停止了定时任务的执行了。


好了,就到这里吧,如果大家有疑问可以随时联系楼主这个小学生,楼主的代码里也许会有不合理的地方,欢迎大家指出、相互交流。

楼主QQ:752058871