传统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();
}
}
}
运行结果如下所示:
结果显示定时任务成功启动,并且正确执行。这里我们可以修改一下测试类,将我们需要的定时任务做成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中去,这样我们项目一启动就可以马上执行定时任务了。
想要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~