传统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实现动态定时器_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实现动态定时器_quartz_02