一、Quartz简介
Quartz大致可分为三个主要的核心:
1、调度器Scheduler:是一个计划调度器容器,容器里面可以盛放众多的JobDetail和Trigger,当容器启动后,里面的每个JobDetail都会根据Trigger按部就班自动去执行.
2、任务Job:要执行的具体内容。JobDetail:具体的可执行的调度程序,包含了这个任务调度的方案和策略。
3、触发器Trigger:调度参数的配置,什么时候去执行调度。
可以这么理解它的原理:调度器就相当于一个容器,装载着任务和触发器。任务和触发器又是绑定在一起的,然而一个任务可以对应多个触发器,但一个触发器却只能对应一个任务。当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的任务作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。
二、与spring的整合
本文的用的是quartz-2.2.1与spring-3.2.2。之所以在这里特别对版本作一下说明,是因为spring和quartz的整合对版本是有要求的。spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错。原因主要是:spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了 org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在 quartz2.x系列中org.quartz.CronTrigger变成了接口,从而造成无法用spring的方式配置quartz的触发器 (trigger)。
spring中使用quartz有两种方式,具体请看http://blog.csdn.net/liuxiao723846/article/details/46879077
三、动态整合
上面的整合只能应付简单的需求,但很多时候我们遇到的是需要动态的添加、暂停、修改任务。而spring中所提供的定时任务组件却只能够通过修改xml中trigger的配置才能控制定时任务的时间以及任务的启用或停止,这在带给我们方便的同时也失去了动态配置任务的灵活性。
所以我们就得换种方式来解决。把任务与cronExpression存放在数据库中,最大化减少xml配置,创建一个工厂类,在实际调用时把任务的相关信息通过参数方式传入,由该工厂类根据任务信息来具体执行需要的操作,从而方便我们的动态修改。
1.spring配置(其实只要这一行足矣,去掉了原先"taskJob"、"myTrigger"等配置):
<
bean
id
=
"schedulerFactoryBean"
class
=
"org.springframework.scheduling.quartz.SchedulerFactoryBean"
/>
2、任务执行入口,实现Job接口,类似工厂类:
package com.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.quartz.model.ScheduleJob;
/**
* 定时任务运行工厂
* @author unique
*
*/
public class QuartzJobFactory implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务成功运行");
ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");
System.out.println("任务名称 = [" + scheduleJob.getJobName() + "]");
//根据name 与 group组成的唯一标识来判别该执行何种操作……
}
}
这里我们实现的是无状态的Job,如果要实现有状态的Job在以前是实现StatefulJob接口,在我使用的quartz 2.2.1中,StatefulJob接口已经不推荐使用了,换成了注解的方式,只需要给你实现的Job类加上注解 @DisallowConcurrentExecution即可实现有状态:
/**
* 定时任务运行工厂类
*/
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {...}
3、
创建任务。既然要动态修改任务,那任务就得保存在某个地方,所以我们需要个JavaBean来存放任务信息。
package com.quartz.model;
/**
* 计划任务信息
* @author unique
*/
public class ScheduleJob {
/** 任务id */
private String jobId;
/** 任务名称 */
private String jobName;
/** 任务分组 */
private String jobGroup;
/** 任务状态 0禁用 1启用 2删除*/
private String jobStatus;
/** 任务运行时间表达式 */
private String cronExpression;
/** 任务描述 */
private String desc;
public ScheduleJob() {
super();
}
public ScheduleJob(String jobId, String jobName, String jobGroup,
String jobStatus, String cronExpression, String desc) {
super();
this.jobId = jobId;
this.jobName = jobName;
this.jobGroup = jobGroup;
this.jobStatus = jobStatus;
this.cronExpression = cronExpression;
this.desc = desc;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
public String getJobStatus() {
return jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
接下来这个类是最为主要的,请求地址跳往相对应的方法:
package com.wxapi.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.quartz.QuartzJobFactory;
import com.quartz.model.ScheduleJob;
import com.wxapi.bs.IQuartzBS;
@Controller
@RequestMapping("/quartz")
public class QuartzAction {
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@Autowired
private IQuartzBS quartzBS;
/**
* 任务创建与更新(未存在的就创建,已存在的则更新)
* @param request
* @param response
* @param scheduleJob
* @param model
* @return
*/
@RequestMapping(value="/update", method={RequestMethod.POST,RequestMethod.GET})
public String updateQuartz(HttpServletRequest request,HttpServletResponse response,
@ModelAttribute("scheduleJob") ScheduleJob job,ModelMap model){
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
if(null!=job){
//获取触发器标识
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
//获取触发器trigger
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if(null==trigger){//不存在任务
//创建任务
JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
.withIdentity(job.getJobName(), job.getJobGroup())
.build();
jobDetail.getJobDataMap().put("scheduleJob", job);
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
.getCronExpression());
//按新的cronExpression表达式构建一个新的trigger
trigger = TriggerBuilder.newTrigger()
.withIdentity(job.getJobName(), job.getJobGroup())
.withSchedule(scheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, trigger);
//把任务插入数据库
int result = quartzBS.add(job);
if(result!=0){
model.addAttribute("msg", "您的任务创建成功!");
}else{
model.addAttribute("msg", "您的任务创建失败!");
}
}else{//存在任务
// Trigger已存在,那么更新相应的定时设置
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
.getCronExpression());
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
//更新数据库中的任务
int result = quartzBS.update(job);
if(result==1){
model.addAttribute("msg", "您的任务更新成功!");
}else{
model.addAttribute("msg", "您的任务更新失败!");
}
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return "/warn.jsp";
}
/**
* 暂停任务
* @param request
* @param response
* @param job
* @param model
* @return
*/
@RequestMapping(value="/pause", method={RequestMethod.POST,RequestMethod.GET})
public String pauseQuartz(HttpServletRequest request,HttpServletResponse response,
@ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
try {
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
return "/warn.jsp";
}
/**
* 恢复任务
* @param request
* @param response
* @param scheduleJob
* @param model
* @return
*/
@RequestMapping(value="/resume", method={RequestMethod.POST,RequestMethod.GET})
public String resumeQuartz(HttpServletRequest request,HttpServletResponse response,
@ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
try {
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
return "/warn.jsp";
}
/**
* 删除任务
* @param request
* @param response
* @param scheduleJob
* @param model
* @return
*/
@RequestMapping(value="/delete", method={RequestMethod.POST,RequestMethod.GET})
public String deleteQuartz(HttpServletRequest request,HttpServletResponse response,
@ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
try {
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
return "/warn.jsp";
}
}
在运行工厂类中:
ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");
可获取任务分组和任务名称来确定任务的唯一性,然后在execute方法中通过判断任务分组和任务名来实现你具体的操作。