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
1 package com.zealer.cps.task.value; 2 3 /** 4 * 定时任务封装类 5 * @author xiaohe 6 */ 7 public class ScheduleJob 8 { 9 /** 任务id */ 10 private int jobId; 11 12 /** 任务名称 */ 13 private String jobName; 14 15 /** 任务分组 */ 16 private String jobGroup; 17 18 /** 任务状态 0禁用 1启用 2删除*/ 19 private String jobStatus; 20 21 /** 任务运行时间表达式 */ 22 private String cronExpression; 23 24 /** 任务执行类 */ 25 private String beanClass; 26 27 /** 任务执行方法 */ 28 private String executeMethod; 29 30 /** 任务创建时间 */ 31 private String createTime; 32 33 /** 任务更新时间 */ 34 private String updateTime; 35 36 /** 任务描述 */ 37 private String jobDesc; 38 39 //set与get方法这里省略,大家可以自己生成 40 ...... 41 }
二、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
1 package com.zealer.cps.task.controller; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.HashMap; 6 import java.util.Map; 7 8 import javax.annotation.Resource; 9 import javax.servlet.http.HttpServletRequest; 10 11 import org.slf4j.Logger; 12 import org.slf4j.LoggerFactory; 13 import org.springframework.http.HttpStatus; 14 import org.springframework.http.ResponseEntity; 15 import org.springframework.stereotype.Controller; 16 import org.springframework.ui.Model; 17 import org.springframework.web.bind.annotation.ModelAttribute; 18 import org.springframework.web.bind.annotation.RequestMapping; 19 import org.springframework.web.bind.annotation.RequestMethod; 20 import org.springframework.web.bind.annotation.RequestParam; 21 import org.springframework.web.bind.annotation.ResponseBody; 22 import org.springframework.web.servlet.mvc.support.RedirectAttributes; 23 import com.zealer.cps.base.annotation.Log; 24 import com.zealer.cps.base.constant.AppConstant; 25 import com.zealer.cps.base.controller.BaseController; 26 import com.zealer.cps.base.message.SuccessActionResult; 27 import com.zealer.cps.base.model.vo.PaginationBean; 28 import com.zealer.cps.base.util.HttpUtils; 29 import com.zealer.cps.task.manage.JobMethod; 30 import com.zealer.cps.task.service.QuartzJobService; 31 import com.zealer.cps.task.value.ScheduleJob; 32 import com.zealer.cps.task.value.ScheduleJobItem; 33 import com.zealer.cps.task.value.ScheduleJobReq; 34 35 @Controller 36 @RequestMapping( "/taskController" ) 37 public class TaskController extends BaseController 38 { 39 private static Logger log = LoggerFactory.getLogger( TaskController.class ); 40 41 @Resource( name = "quartzJobService" ) 42 private QuartzJobService quartzJobService; 43 44 @Resource( name = "JobMethod" ) 45 private JobMethod jobMethod; 46 47 @RequestMapping( "/list" ) 48 @Log( "任务列表" ) 49 public String listJob( @ModelAttribute("job") ScheduleJobReq jobReq, Model model, HttpServletRequest request ) 50 { 51 PaginationBean<ScheduleJob> pb = quartzJobService.getJobsByPage( jobReq ); 52 try { 53 pb.setUrl( HttpUtils.getRequestInfo( request, true ) ); 54 } catch ( Exception e ) { 55 log.error( "get request url error", e ); 56 } 57 model.addAttribute( "pb", pb ); 58 return("task/taskList"); 59 } 60 61 62 /** 63 * 立即执行定时任务 64 * @param job 任务实体 65 * @param model 66 * @return 67 */ 68 @ResponseBody 69 @RequestMapping( value = "/executeJob", produces = "application/json;charset=utf-8" ) 70 @Log( "立即执行任务" ) 71 public ResponseEntity<Map<String, Object> > executeJob( ScheduleJob job, Model model ) 72 { 73 jobMethod.runJobNow( job ); 74 return(new ResponseEntity<Map<String, Object> > ( new HashMap<String, Object>(), HttpStatus.OK ) ); 75 } 76 77 78 /** 79 * 跳转到添加定时任务的页面 80 * @param model 81 * 储存结果的实体 82 */ 83 @RequestMapping( value = "/addJob", method = RequestMethod.GET ) 84 @Log( "初始化添加表单" ) 85 public String addForm( Model model ) 86 { 87 model.addAttribute( "job", new ScheduleJob() ); 88 return("task/addJob"); 89 } 90 91 92 /** 93 * 添加定时任务记录 94 * @param job 任务实体 95 */ 96 @RequestMapping( value = "/addJob", method = RequestMethod.POST ) 97 @Log( "新增操作员" ) 98 public String addUser( @ModelAttribute("job") ScheduleJob job, RedirectAttributes ra, Model model, 99 HttpServletRequest request ) 100 { 101 SimpleDateFormat format = new SimpleDateFormat( AppConstant.DATE_FORMAT_YYYYMMDDHHMMSS ); 102 job.setCreateTime( format.format( new Date() ) ); 103 quartzJobService.inserJob( job ); 104 ra.addFlashAttribute( "actionResult", new SuccessActionResult() ); 105 return("redirect:/taskController/list.do"); 106 } 107 108 109 /** 110 * 初始化修改表单 111 * @param jobId 112 * @return 跳转地址 113 */ 114 @RequestMapping( value = "/updateJob", method = RequestMethod.GET ) 115 @Log( "初始化修改表单" ) 116 public String updateForm( @RequestParam("id") Integer jobId, Model model, 117 HttpServletRequest request ) 118 { 119 ScheduleJob job = quartzJobService.getScheduleJobById( jobId ); 120 model.addAttribute( "job", job ); 121 return("task/updateJob"); 122 } 123 124 125 /** 126 * 修改定时任务记录信息 127 * @param job 待修改的操作员实体 128 * @param model 封装处理结果的实体 129 * @param request 请求对象 130 * @return 跳转地址 131 */ 132 @RequestMapping( value = "/updateJob", method = RequestMethod.POST ) 133 @Log( "修改定时任务" ) 134 public String updateJob( @ModelAttribute ScheduleJob job, RedirectAttributes ra, Model model, 135 HttpServletRequest request ) 136 { 137 SimpleDateFormat format = new SimpleDateFormat( AppConstant.DATE_FORMAT_YYYYMMDDHHMMSS ); 138 job.setUpdateTime( format.format( new Date() ) ); 139 quartzJobService.updateJob( job ); 140 ra.addFlashAttribute( "actionResult", new SuccessActionResult() ); 141 return("redirect:/taskController/list.do"); 142 } 143 144 145 /** 146 * 删除一条定时任务记录信息 147 * @return 148 */ 149 @RequestMapping( value = "/deleteJob" ) 150 @Log( "删除任务" ) 151 public String deleteJob( @RequestParam("id") int jobId, RedirectAttributes ra ) 152 { 153 quartzJobService.deleteJob( jobId ); 154 ra.addFlashAttribute( "actionResult", new SuccessActionResult() ); 155 return("redirect:/taskController/list.do"); 156 } 157 158 159 /** 160 * 校验执行任务的表达式是否正确 161 * @param expression 162 * @return 163 */ 164 @ResponseBody 165 @RequestMapping( value = "/checkExp", produces = "application/json;charset=utf-8" ) 166 @Log( "校验任务表达式" ) 167 public ResponseEntity<Map<String, Object> > checkExpression( String expression ) 168 { 169 Map<String, Object> map = new HashMap<String, Object>(); 170 map.put( AppConstant.SYSTEM_JSON_CODE, AppConstant.SYSTEM_JSON_ERROR ); 171 if ( jobMethod.checkCron( expression ) ) 172 { 173 map.put( AppConstant.SYSTEM_JSON_CODE, AppConstant.SYSTEM_JSON_SUCCESS ); 174 } 175 return(new ResponseEntity<Map<String, Object> > ( map, HttpStatus.OK ) ); 176 } 177 178 179 /** 180 * 某个定时任务下的所有执行记录信息列表 181 * @param jobReq 182 * @return 183 */ 184 @RequestMapping( "/itemJob" ) 185 @Log( "任务执行信息列表" ) 186 public String executeJobList( @ModelAttribute("job") ScheduleJobReq jobReq, int jobId, 187 Model model, HttpServletRequest request ) 188 { 189 PaginationBean<ScheduleJobItem> pb = quartzJobService.getJobItemsByPage( jobId, jobReq ); 190 try { 191 pb.setUrl( HttpUtils.getRequestInfo( request, true ) ); 192 } catch ( Exception e ) { 193 log.error( "get request url error", e ); 194 } 195 model.addAttribute( "pb", pb ); 196 model.addAttribute( "jobId", jobId ); 197 return("task/taskItemList"); 198 } 199 }