传统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"
>
<
bean
id
="startQuertz"
lazy-init
="true"
autowire
="no"
class
="org.springframework.scheduling.quartz.
SchedulerFactoryBean
"
>
bean
>
<
bean
id
="quartzManager"
class
="com.infun.platform.quartz.QuartzManager"
lazy-init
="false"
init-method
="startJobs"
>
<
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 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 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中去,这样我们项目一启动就可以马上执行定时任务了。
想要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~