传统Spring+Quartz实现的定时器一般都是通过配置文件中配置的,这就意味着如果我们修改了定时任务的时间,就得重新启动一下Tomcat,而且每次新建一个任务就得在spring中添加相关的配置信息,非常麻烦。基于上面的问题,这边给大家介绍一下Spring+Quartz如何实现动态定时器,如何避免创建多个定时任务的配置文件。
1.在开始之前,我们需求引入Quartz相关的依赖,pom.xml文件如下所示:
<!-- 定时任务 -->
<dependency>
<groupId > org.springframework </ groupId>
<artifactId > spring-context-support </ artifactId>
<version > 4.2.4.RELEASE </ version>
</dependency>
<dependency>
<groupId > org.quartz-scheduler </ groupId>
<artifactId > quartz </ artifactId>
<version > 2.2.1 </ version>
</dependency>
<dependency>
<groupId > org.quartz-scheduler </ groupId>
<artifactId > quartz-jobs </ artifactId>
<version > 2.2.1 </ version>
</dependency>
2.我们需要在配置文件中配置定时任务的容器,也就是SchedulerFactoryBean,然后再配置一个定时任务管理器,这个类是我们自己定义的,而上面的SchedulerFactoryBean是来自依赖中的库。具体代码如下所示:
<? 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: mongo ="http://www.springframework.org/schema/data/mongo"
xsi :schemaLocation ="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
<!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->
< bean id ="startQuertz" lazy-init ="true" autowire ="no" class ="org.springframework.scheduling.quartz. SchedulerFactoryBean " ></ bean>
 
<!--这个类是用来设置触发时间的, startJobs方法启动调度容器,然后按照上面触发器每隔1s执行所配置的myJob2.doSomething()方法 -->
< bean id ="quartzManager" class ="com.infun.platform.quartz.QuartzManager" lazy-init ="false" init-method ="startJobs" >
<!--这个对象一定要注入,这样类才能进行管理,还有在类型要用get set方法,不然会报错。-->
< property name ="scheduler" ref ="startQuertz" />
</bean>
</beans>
这边我给这个配置文件取名为quartz.xml
3.第三步就是重中之重的,定时任务管理器的编写了,它负责去创建定时任务、修改任务触发事件、删除任务、停止定时任务容器等功能。下面我们具体看代码:
package com.infun.platform.quartz;
 
import org.quartz.*;
 
/**
* 定时任务配置管理中心
*
* @author linzhiqiang
* @date 2018/6/5
*/
public class QuartzManager {
private Scheduler scheduler;
 
/**
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务
* @param cron 时间设置,参考quartz说明文档
* @param quartzTask task参数
* @Description: 添加一个定时任务
*/
@SuppressWarnings ({ "unchecked" , "rawtypes"})
public void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass, String cron,TaskData quartzTask) {
try {
JobDataMap jobMap=new JobDataMap();
jobMap.put("task", quartzTask);
// 任务名,任务组,任务执行类
JobDetail jobDetail = JobBuilder. newJob(jobClass).withIdentity(jobName, jobGroupName)
.usingJobData(jobMap)
.build();
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder. newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder. cronSchedule(cron));
// 创建Trigger对象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (! scheduler .isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
 
/**
* @param jobName
* @param jobGroupName
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cron 时间设置,参考quartz说明文档
* @Description: 修改一个任务的触发时间
*/
public void modifyJobTime(String jobName,
String jobGroupName, String triggerName, String triggerGroupName, String cron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null ) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
/** 方式一 :调用 rescheduleJob 开始 */
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder. newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder. cronSchedule(cron));
// 创建Trigger对象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一个任务的触发时间
scheduler.rescheduleJob(triggerKey, trigger);
/** 方式一 :调用 rescheduleJob 结束 */
/** 方式二:先删除,然后在创建一个新的Job */
//JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//Class<? extends Job> jobClass = jobDetail.getJobClass();
//removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
//addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
/** 方式二 :先删除,然后在创建一个新的Job */
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
 
/**
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
* @Description: 移除一个任务
*/
public void removeJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
scheduler .pauseTrigger(triggerKey); // 停止触发器
scheduler .unscheduleJob(triggerKey); // 移除触发器
scheduler .deleteJob(JobKey. jobKey (jobName, jobGroupName)); // 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
 
/**
* @Description:启动所有定时任务
*/
public void startJobs() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
 
/**
* @Description:关闭所有定时任务
*/
public void shutdownJobs() {
try {
if (! scheduler .isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
 
public Scheduler getScheduler() {
return scheduler;
}
 
public void setScheduler(Scheduler scheduler) {
this . scheduler = scheduler;
}
}
4.最后就是具体任务类的编写了,这边有几种方式:一种是你所有的定时任务都写再一个类里面,通过if去判断,二是你写多个任务类直接启动相关任务类就可以了,三是你就写一个任务类,但是不是通过if去判断,而是通过接口实现类去实现这个接口。关于三种方式,我推荐第三种,因为第三者可扩展性最高。下面我们具体看一下任务类的实现:
package com.infun.platform.quartz;
 
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
/**
*
* 小队定时任务
* @author linzhiqiang
* @date 2018/6/4
*/
public class QuartzTask implements Job {
@Override
public void execute( JobExecutionContext jobExecutionContext) throws JobExecutionException {
//执行任务
JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();
TaskData quartzTask = (TaskData)dataMap.get("task");
// 调用接口函数
quartzTask.execute();
}
}
任务的参数接口代码:
package com.infun.platform.quartz;
 
/**
* 任务接口,接口具体实现由调用者来实现
* @author linzhiqiang
* @date 2018/6/5
*/
public interface TaskData {
/**
* 任务具体的执行方法
*/
public void execute();
}
任务的参数接口实现类代码:
package com.infun.platform.quartz;
 
/**
*
* @author linzhiqiang
* @date 2018/6/5
*/
public class TaskDataImpl implements TaskData {
@Override
public void execute() {
System.out .println( "定时任务。。。。。。。。。。。。执行中。。。。。");
}
}
最后我们写一个测试类来测试一下,定时任务有没有生效,测试类如下所示:
package com.infun.platform.quartz;
 
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
/**
*
* @author linzhiqiang
* @date 2018/6/5
*/
public class Test {
public static void main(String[] args) throws BeansException {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "quartz.xml");
QuartzManager quartzManager = (QuartzManager) ctx.getBean("quartzManager");
try {
System.out .println( "【系统启动】开始(每1秒输出一次 job2)...");
Thread.sleep(5000);
System.out .println( "【增加job1启动】开始(每1秒输出一次)...");
quartzManager.addJob("test" , "test" , "test" , "test" , QuartzTask. class , "0/1 * * * * ?" , new TaskDataImpl());
 
Thread.sleep(5000);
System.out .println( "【修改job1时间】开始(每2秒输出一次)...");
quartzManager.modifyJobTime("test" , "test" , "test" , "test" , "0/2 * * * * ?");
//
Thread. sleep(10000);
System.out .println( "【移除job1定时】开始...");
quartzManager.removeJob("test" , "test" , "test" , "test");
 
// 关掉任务调度容器
quartzManager.shutdownJobs();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下所示:
Spring整合Quartz实现动态定时器_Spring+Quartz
结果显示定时任务成功启动,并且正确执行。这里我们可以修改一下测试类,将我们需要的定时任务做成API暴露给其它模块来调用,API类如下所示:
package com.infun.platform.quartz;
 
/**
*
* 任务调度服务
* @author linzhiqiang
* @date 2018/6/5
*/
public class QuartzService {
public void setQuartzManager(QuartzManager quartzManager) {
this . quartzManager = quartzManager;
}
private QuartzManager quartzManager;
 
/**
* 启动定时任务
* @param jobClass 任务的task类
* @param cron 定时任务的表达式
*/
public void startTask(Class jobClass, String cron,TaskData quartzTask){
String jobName = jobClass.getName();
quartzManager.addJob(jobName, jobName, jobName, jobName, jobClass, cron, quartzTask);
}
 
/**
* 修改定时任务时间
* @param jobName 任务的task名称
* @param cron 定时任务的表达式
*/
public void updateTaskTime(String jobName, String cron){
quartzManager.modifyJobTime(jobName,jobName,jobName,jobName, cron);
}
 
/**
* 移除定时任务
* @param jobName 任务的task名称
* @param cron 定时任务的表达式
*/
public void removeTask(String jobName, String cron){
quartzManager.removeJob(jobName,jobName,jobName,jobName);
}
 
/**
* 关闭定时任务容器
* 慎用
*/
public void shutdownJobs(){
// 关掉任务调度容器
quartzManager.shutdownJobs();
}
}

Spring整合Quartz实现动态定时器就全部结束了,我们可以将配置文件加入到web.xml中去,这样我们项目一启动就可以马上执行定时任务了。

 

Spring整合Quartz实现动态定时器_Spring+Quartz_02