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

大家好,小学生又来为初级者分享一下技术了。这节我们来分享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 '描述'
);

手动输入一条数据如下图



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










  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值