spring3整合quartz2,实现动态添加、修改、暂停、重启定时任务

时间:2021-05-05 00:20:34

一、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方法中通过判断任务分组和任务名来实现你具体的操作。