Spring 3整合Quartz 2实现手动设置定时任务:新增,修改,删除,暂停和恢复----每一个你不满意的当下,都有一个你不曾努力的过去

Posted 沧海一滴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 3整合Quartz 2实现手动设置定时任务:新增,修改,删除,暂停和恢复----每一个你不满意的当下,都有一个你不曾努力的过去相关的知识,希望对你有一定的参考价值。

 ---每一个你不满意的当下,都有一个你不曾努力的过去---

摘要:在项目的管理功能中,对定时任务的管理有时会很常见。但一般定时任务配置都在xml中完成,包括cronExpression表达式,十分的方便。但是如果我的任务信息是保存在数据库的,想要动态的初始化,还有就是任务较多的时候不是得有一大堆的xml配置?或者说我要修改一下trigger的表达式,使原来5秒运行一次的任务变成10秒运行一次,或者说我要控制定时任务的 “ 暂停 ” 呢?暂停之后又要在某个时间点 “ 重启 ” 该定时任务呢?或者说直接 “ 删除 ” 该定时任务呢?要 改变某定时任务的触发时间呢?这时问题就来了,试过在配置文件中不传入cronExpression等参数,但是启动时就报错了,难道我每次都修改xml文件然后重启应用吗,这显然不合适的。

最理想的是在与spring整合的同时又能实现动态任务的添加、删除及修改配置,而且不需要重启应用。

 首先我们来回顾一下spring中使用quartz的配置代码:

<!-- 使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一个任务执行完后再开启新的任务-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
</bean>
<!--  调度触发器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>
<!-- 调度工厂 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>

 

所有的配置都在xml中完成,包括cronExpression表达式,十分的方便。但是如果定时任务一多并且需要手动变化时,就得有一大堆的xml配置,不方便管理。

于是在设计时我想到以下几点

1、减少spring的配置文件。

2、用户可以通过页面等方式添加、启用、禁用某个任务。

3、用户可以修改某个已经在运行任务的运行时间表达式,即CronExpression。

4、为方便维护,简化任务的运行调用处理,任务的运行入口即Job实现类最好只有一个,该Job运行类相当于工厂类,在实际调用时把任务的相关信息通过参数方式传入,由该工厂类根据任务信息来具体执行需要的操作。

就像如下图所示:

1、可在页面直接查看任务详情

 

2、可添加、修改

3、可立即执行,并查看执行详情

 

在上面的思路下来进行我们的开发吧。

一、初始化用的数据库脚本

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for task_detail
-- ----------------------------
DROP TABLE IF EXISTS `task_detail`;
CREATE TABLE `task_detail` (
  `job_id` int(100) NOT NULL AUTO_INCREMENT,
  `job_name` varchar(200) DEFAULT NULL COMMENT \'任务名称\',
  `job_group` varchar(100) DEFAULT NULL COMMENT \'任务分组\',
  `job_status` varchar(5) DEFAULT NULL COMMENT \'任务状态 0禁用 1启用 2删除\',
  `cron_expression` varchar(200) DEFAULT NULL COMMENT \'任务运行时间表达式\',
  `bean_class` varchar(300) DEFAULT NULL COMMENT \'任务执行类\',
  `execute_method` varchar(100) DEFAULT NULL COMMENT \'任务执行方法\',
  `create_time` date DEFAULT NULL COMMENT \'任务创建时间\',
  `update_time` date DEFAULT NULL COMMENT \'任务更新时间\',
  `job_desc` varchar(500) DEFAULT NULL COMMENT \'任务描述\',
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of task_detail
-- ----------------------------
INSERT INTO `task_detail` VALUES (\'9\', \'测试手动设置任务\', \'testQuartzTask\', null, \'0 0 1 * * ?\', \'com.zealer.cps.task.executor.TestQuartzTask\', \'printOneWord\', \'2017-06-22\', \'2017-06-22\', \'打印一句话\');

然后创建对应的实体类ScheduleJob.java

package com.zealer.cps.task.value;

/**
 * 定时任务封装类
 * @author   xiaohe
 */
public class ScheduleJob
{
    /** 任务id */
    private int jobId;
    
    /** 任务名称 */
    private String jobName;
    
    /** 任务分组 */
    private String jobGroup;
    
    /** 任务状态 0禁用 1启用 2删除*/
    private String jobStatus;
    
    /** 任务运行时间表达式 */
    private String cronExpression;
    
    /** 任务执行类 */
    private String beanClass;
    
    /** 任务执行方法 */
    private String executeMethod;
    
    /** 任务创建时间 */
    private String createTime;
    
    /** 任务更新时间 */
    private String updateTime;
    
    /** 任务描述 */
    private String jobDesc;
   
    //set与get方法这里省略,大家可以自己生成
    ......  
}

 

二、spring配置文件spring.xml

<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

然后在web.xml加入

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
</context-param>

三、编写任务控制器TaskController.java

package com.zealer.cps.task.controller;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.zealer.cps.base.annotation.Log;
import com.zealer.cps.base.constant.AppConstant;
import com.zealer.cps.base.controller.BaseController;
import com.zealer.cps.base.message.SuccessActionResult;
import com.zealer.cps.base.model.vo.PaginationBean;
import com.zealer.cps.base.util.HttpUtils;
import com.zealer.cps.task.manage.JobMethod;
import com.zealer.cps.task.service.QuartzJobService;
import com.zealer.cps.task.value.ScheduleJob;
import com.zealer.cps.task.value.ScheduleJobItem;
import com.zealer.cps.task.value.ScheduleJobReq;

@Controller
@RequestMapping( "/taskController" )
public class TaskController extends BaseController
{
    private static Logger log = LoggerFactory.getLogger( TaskController.class );

    @Resource( name = "quartzJobService" )
    private QuartzJobService quartzJobService;

    @Resource( name = "JobMethod" )
    private JobMethod jobMethod;

    @RequestMapping( "/list" )
    @Log( "任务列表" )
    public String listJob( @ModelAttribute("job") ScheduleJobReq jobReq, Model model, HttpServletRequest request )
    {
        PaginationBean<ScheduleJob> pb = quartzJobService.getJobsByPage( jobReq );
        try {
            pb.setUrl( HttpUtils.getRequestInfo( request, true ) );
        } catch ( Exception e ) {
            log.error( "get request url error", e );
        }
        model.addAttribute( "pb", pb );
        return("task/taskList");
    }


    /**
     * 立即执行定时任务
     * @param job 任务实体
     * @param model
     * @return
     */
    @ResponseBody
    @RequestMapping( value = "/executeJob", produces = "application/json;charset=utf-8" )
    @Log( "立即执行任务" )
    public ResponseEntity<Map<String, Object> > executeJob( ScheduleJob job, Model model )
    {
        jobMethod.runJobNow( job );
        return(new ResponseEntity<Map<String, Object> > ( new HashMap<String, Object>(), HttpStatus.OK ) );
    }


    /**
     * 跳转到添加定时任务的页面
     * @param model
     *            储存结果的实体
     */
    @RequestMapping( value = "/addJob", method = RequestMethod.GET )
    @Log( "初始化添加表单" )
    public String addForm( Model model )
    {
        model.addAttribute( "job", new ScheduleJob() );
        return("task/addJob");
    }


    /**
     * 添加定时任务记录
     * @param job 任务实体
     */
    @RequestMapping( value = "/addJob", method = RequestMethod.POST )
    @Log( "新增操作员" )
    public String addUser( @ModelAttribute("job") ScheduleJob job, RedirectAttributes ra, Model model,
                   HttpServletRequest request )
    {
        SimpleDateFormat format = new SimpleDateFormat( AppConstant.DATE_FORMAT_YYYYMMDDHHMMSS );
        job.setCreateTime( format.format( new Date() ) );
        quartzJobService.inserJob( job );
        ra.addFlashAttribute( "actionResult", new SuccessActionResult() );
        return("redirect:/taskController/list.do");
    }


    /**
     * 初始化修改表单
     * @param jobId
     * @return 跳转地址
     */
    @RequestMapping( value = "/updateJob", method = RequestMethod.GET )
    @Log( "初始化修改表单" )
    public String updateForm( @RequestParam("id") Integer jobId, Model model,
                  HttpServletRequest request )
    {
        ScheduleJob job = quartzJobService.getScheduleJobById( jobId );
        model.addAttribute( "job", job );
        return("task/updateJob");
    }


    /**
     * 修改定时任务记录信息
     * @param job 待修改的操作员实体
     * @param model 封装处理结果的实体
     * @param request 请求对象
     * @return 跳转地址
     */
    @RequestMapping( value = "/updateJob", method = RequestMethod.POST )
    @Log( "修改定时任务" )
    public String updateJob( @ModelAttribute ScheduleJob job, RedirectAttributes ra, Model model,
                 HttpServletRequest request )
    {
        SimpleDateFormat format = new SimpleDateFormat( AppConstant.DATE_FORMAT_YYYYMMDDHHMMSS );
        job.setUpdateTime( format.format( new Date() ) );
        quartzJobService.updateJob( job );
        ra.addFlashAttribute( "actionResult", new SuccessActionResult() );
        return("redirect:/taskController/list.do");
    }


    /**
     * 删除一条定时任务记录信息
     * @return
     */
    @RequestMapping( value = "/deleteJob" )
    @Log( "删除任务" )
    public String deleteJob( @RequestParam("id") int jobId, RedirectAttributes ra )
    {
        quartzJobService.deleteJob( jobId );
        ra.addFlashAttribute( "actionResult", new SuccessActionResult() );
        return("redirect:/taskController/list.do");
    }


    /**
     * 校验执行任务的表达式是否正确
     * @param expression
     * @return
     */
    @ResponseBody
    @RequestMapping( value = "/checkExp", produces = "application/json;charset=utf-8" )
    @Log( "校验任务表达式" )
    public ResponseEntity<Map<String, Object> > checkExpression( String expression )
    {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put( AppConstant.SYSTEM_JSON_CODE, AppConstant.SYSTEM_JSON_ERROR );
        if ( jobMethod.checkCron( expression ) )
        {
            map.put( AppConstant.SYSTEM_JSON_CODE, AppConstant.SYSTEM_JSON_SUCCESS );
        }
        return(new ResponseEntity<Map<String, Object> > ( map, HttpStatus.OK ) );
    }


    /**
     * 某个定时任务下的所有执行记录信息列表
     * @param jobReq
     * @return
     */
    @RequestMapping( "/itemJob" )
    @Log( "任务执行信息列表" )
    public String executeJobList( @ModelAttribute("job") ScheduleJobReq jobReq, int jobId,
                      Model model, HttpServletRequest request )
    {
        PaginationBean<ScheduleJobItem> pb = quartzJobService.getJobItemsByPage( jobId, jobReq );
        try {
            pb.setUrl( HttpUtils.getRequestInfo( request, true ) );
        } catch ( Exception e ) {
            log.error( "get request url error", e );
        }
        model.addAttribute( "pb", pb );
        model.addAttribute( "jobId", jobId );
        return("task/taskItemList");
    }
}

 

四、编写任务运行入口,即JobMethod.java

package com.zealer.cps.task.manage;

import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.stereotype.Component;

import com.zealer.cps.task.QuartzJobFactory;
import com.zealer.cps.task.service.QuartzJobService;
import com.zealer.cps.task.value.ScheduleJob;


/**
 * 提供Job任务相关的方法
 * @author xiaohe
 */
@Component( "JobMethod" )
public class JobMethod
{
    @Resource( name = "schedulerFactoryBean" )
    private Scheduler scheduler;

    @Resource( name = "quartzJobService" )
    private QuartzJobService quartzJobService;

    private static Log log = LogFactory.getLog( JobMethod.class );


    /**
     * 任务框架初始化方法
     * @throws
     */
    public void init()
    {
        /* 从数据库获得所有的任务信息记录 */
        List<ScheduleJob> jobList = quartzJobService.getAllJobs();

        if ( jobList != null && !jobList.isEmpty() )
        {
            for ( ScheduleJob scheduleJob : jobList )
            {
                /* 判断任务状态,是否为执行状态 */

                TriggerKey triggerKey = TriggerKey.triggerKey( scheduleJob
                                     .getJobName(), scheduleJob.getJobGroup() );
                CronTrigger trigger;
                try
                {
                    trigger = (CronTrigger) scheduler.getTrigger( triggerKey );
                    if ( null == trigger )
                    {
                        JobDetail jobDetail = JobBuilder.newJob(
                            QuartzJobFactory.class ).withIdentity(
                            scheduleJob.getJobName(),
                            scheduleJob.getJobGroup() ).build();

                        jobDetail.getJobDataMap().put( "scheduleJob",
                                     scheduleJob );

                        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                                         .cronSchedule( scheduleJob.getCronExpression() );

                        trigger = TriggerBuilder.newTrigger().withIdentity(
                            scheduleJob.getJobName(),
                            scheduleJob.getJobGroup() ).withSchedule(
                            scheduleBuilder ).build();
                        scheduler.scheduleJob( jobDetail, trigger );
                    }else {
                        /* Trigger已存在,那么更新相应的定时设置 */
                        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                                         .cronSchedule( scheduleJob.getCronExpression() );

                        /* 按新的cronExpression表达式重新构建trigger */
                        trigger = trigger.getTriggerBuilder().withIdentity(
                            triggerKey ).withSchedule( scheduleBuilder )
                             .build();

                        /* 按新的trigger重新设置job执行 */
                        scheduler.rescheduleJob( triggerKey, trigger );
                    }
                }
                catch ( SchedulerException e )
                {
                    log.error( "Task init failed.", e );
                }
            }
        }
    }


    /**
     * 暂停一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void pauseJob( ScheduleJob scheduleJob )
    {
        JobKey jobKey = JobKey.jobKey( scheduleJob.getJobName(), scheduleJob.getJobGroup() );
        try
        {
            scheduler.pauseJob( jobKey );
        }
        catch ( SchedulerException e )
        {
            log.error( "Task pause failed.", e );
        }
    }


    /**
     * 恢复一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void resumeJob( ScheduleJob scheduleJob )
    {
        JobKey jobKey = JobKey.jobKey( scheduleJob.getJobName(), scheduleJob.getJobGroup() );
        try
        {
            scheduler.resumeJob( jobKey );
        }
        catch ( SchedulerException e )
        {
            log.error( "Task resume failed.", e );
        }
    }


    /**
     * 删除一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void deleteJob( ScheduleJob scheduleJob )
    {
        JobKey jobKey = JobKey.jobKey( scheduleJob.getJobName(), scheduleJob.getJobGroup() );
        try
        {
            scheduler.deleteJob( jobKey );
        }
        catch ( SchedulerException e )
        {
            log.error( "Task delete failed.", e );
        }
    }


    /**
     * 立即执行job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void runJobNow( ScheduleJob scheduleJob )
    {
        JobKey jobKey = JobKey.jobKey( scheduleJob.getJobName(), scheduleJob.getJobGroup() );
        try
        {
            scheduler.triggerJob( jobKey );
        }
        catch ( SchedulerException e )
        {
            log.error( "Task run failed.", e );
        }
    }


    /**
     * 更新job时间表达式
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public void updateJobCron( ScheduleJob scheduleJob ) throws SchedulerException
    {
        TriggerKey triggerKey = TriggerKey.triggerKey( scheduleJob.getJobName(),
                             scheduleJob.getJobGroup() );
        /* 获取trigger,即在spring配置文件中定义的 bean id="schedulerFactoryBean" */
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger( triggerKey );
        /* 表达式调度构建器 */
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule( scheduleJob
                                            .getCronExpression() );
        /*按新的cronExpression表达式重新构建trigger */
        trigger = trigger.getTriggerBuilder().withIdentity( triggerKey )
             .withSchedule( scheduleBuilder ).build();
        /*按新的trigger重新设置job执行 */
        scheduler.rescheduleJob( triggerKey, trigger );
    }


/**
 * 判断表达式是否可用
 * @param cron
 * @return
 * @throws
 */
    public boolean checkCron( String cron )
    {
        try
        {
            CronScheduleBuilder.cronSchedule( cron );
        }
        catch ( Exception e )
        {
            return(false);
        }
        return(true);
    }
}

 

 

五、编写业务层类QuartzJobService.java

package com.zealer.cps.task.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.zealer.cps.base.dao.BaseDaoInterface;
import com.zealer.cps.base.model.vo.PaginationBean;
import com.zealer.cps.task.value.ScheduleJob;
import com.zealer.cps.task.value.ScheduleJobItem;
import com.zealer.cps.task.value.ScheduleJobReq;

@Service( "quartzJobService" )
public class QuartzJobService
{
    public static final String    JOB_LIST    = "quartzJob.jobsList";
    public static final String    JOB_SELECT_BYID = "quartzJob.selectById";
    public static final String    JOB_INSERT    = "quartzJob.addJob";
    public static final String    JOB_UPDATE    = "quartzJob.updateJob";
    public static final String    JOB_DELETE    = "quartzJob.deleteJob";
    public static final String    JOB_LIST_PAGE    = "quartzJob.jobListPage";

    public static final String    JOBITEM_LIST_PAGE    = "jobItem.selectListPageByMap";
    public static final String    JOBITEM_INSERT        = "jobItem.insertJobItem";
    public static final String    JOBITEM_SELETE_BYID    = "jobItem.selectByPrimaryKey";

    @Resource( name = "mybatisBaseDao" )
    private BaseDaoInterface baseDao;


    /**
     * 获取所有的定时任务记录信息
     * @return
     */
    public List<ScheduleJob> getAllJobs()
    {
        return(this.baseDao.queryForList( JOB_LIST, null ) );
    }


    /**
     * 根据id获取任务记录
     * @param id
     * @return
     */
    public ScheduleJob getScheduleJobById( int id )
    {
        以上是关于Spring 3整合Quartz 2实现手动设置定时任务:新增,修改,删除,暂停和恢复----每一个你不满意的当下,都有一个你不曾努力的过去的主要内容,如果未能解决你的问题,请参考以下文章

项目ITP spring4.0 整合 Quartz 实现动态任务调度

Spring Boot 应用系列 6 -- Spring Boot 2 整合Quartz

Spring Boot 整合 Quartz 实现定时任务

spring与quartz整合实现分布式动态创建,删除,改变执行时间定时任务(mysql数据库)

Spring整合Quartz实现动态定时器

spring boot 整合 quartz 集群环境 实现 动态定时任务配置原