Spring+Quartz实现动态添加定时任务
概述
详细
一、准备工作
1.java环境搭建,具体参考包中的webapp/resources/doc/平台开发环境安装Guide_V1.0.docx文档
2.使用源码中的webapp/resources/doc/init.sql初始化表结构及数据
t_timetask 任务表
t_timetask_log 任务运行日志
3.数据库连接配置在cloud_parent中的pom.xml中,数据库名称ffxl_cloud,默认账号root,密码123456,同样可在pom.xml中修改
4.运行quartz项目,此处注意,使用的端口号需要与platform_parent下pom.xml中的quartz.job.url的一致,程序中用的是8080端口,具体使用哪个配置,请参考maven中profiles的使用
5.运行admin项目,注意,此处端口要与quartz不同,程序中用的是80端口,浏览器中输入http://localhost/admin 运行结果如图:
二、代码引入
1、文件引入顺序:lib_parent → platform_parent → cloud_parent
2、代码结构
三、程序实现
quartz项目部分代码
1.quartz项目启动时,初始化数据库中的定时任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | package com.ffxl.quartz.init; import java.util.ArrayList; import java.util.List; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.STimetaskExample; import com.ffxl.cloud.model.base.BaseSTimetaskExample.Criteria; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.cloud.service.STimetaskService; import com.ffxl.quartz.task.util.QuartzJobFactory; import com.ffxl.quartz.task.util.QuartzJobFactoryDisallowConcurrentExecution; /** * 根据上下文获取spring类 * * @author */ public class InitQuartzJob implements ApplicationContextAware{ private static final Logger logger = LoggerFactory.getLogger(InitQuartzJob. class ); private static ApplicationContext appCtx; public static SchedulerFactoryBean schedulerFactoryBean = null ; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if ( this .appCtx == null ) { this .appCtx = applicationContext; } } public static void init() { schedulerFactoryBean = (SchedulerFactoryBean) appCtx.getBean(SchedulerFactoryBean. class ); Scheduler scheduler = schedulerFactoryBean.getScheduler(); try { logger.info(scheduler.getSchedulerName()); } catch (SchedulerException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // 这里从数据库中获取任务信息数据 STimetaskService sTimetaskService = (STimetaskService) appCtx.getBean(STimetaskService. class ); STimetaskExample example = new STimetaskExample(); Criteria c = example.createCriteria(); c.andJobStatusEqualTo( "1" ); // 已发布的定时任务 List<STimetask> list = sTimetaskService.selectByExample(example); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(); for (STimetask sTimetask : list) { ScheduleJob job1 = new ScheduleJob(); job1.setJobId(sTimetask.getId()); job1.setJobGroup(sTimetask.getGroupName()); // 任务组 job1.setJobName(sTimetask.getName()); // 任务名称 job1.setJobStatus(sTimetask.getJobStatus()); // 任务发布状态 job1.setIsConcurrent(sTimetask.getConcurrent() ? "1" : "0" ); // 运行状态 job1.setCronExpression(sTimetask.getCron()); job1.setBeanClass(sTimetask.getBeanName()); // 一个以所给名字注册的bean的实例 job1.setMethodName(sTimetask.getMethodName()); job1.setJobData(sTimetask.getJobData()); // 参数 jobList.add(job1); } for (ScheduleJob job : jobList) { try { addJob(job); } catch (SchedulerException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 添加任务 * * @param scheduleJob * @throws SchedulerException */ public static void addJob(ScheduleJob job) throws SchedulerException { if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) { return ; } Scheduler scheduler = schedulerFactoryBean.getScheduler(); logger.debug(scheduler + "...........................................add" ); TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 不存在,创建一个 if ( null == trigger) { Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory. class : QuartzJobFactoryDisallowConcurrentExecution. class ; JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).usingJobData( "data" , job.getJobData()).build(); jobDetail.getJobDataMap().put( "scheduleJob" , job); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); trigger = TriggerBuilder.newTrigger().withDescription(job.getJobId().toString()).withIdentity(job.getJobName(), job.getJobGroup()) .withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger); } else { // Trigger已存在,那么更新相应的定时设置 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); // 按新的cronExpression表达式重新构建trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).usingJobData( "data" , job.getJobData()).withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(triggerKey, trigger); } } } |
2.提供job对应的操作服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | package com.ffxl.quartz.task; import java.io.BufferedInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONArray; import org.apache.log4j.Logger; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerKey; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject; import com.ffxl.cloud.annotation.ControllerLogAnnotation; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.platform.util.JsonResult; import com.ffxl.platform.util.StringUtil; import com.ffxl.quartz.init.InitQuartzJob; @Component @RequestMapping (value = "/opt" ) public class JobSerlvet { public final Logger log = Logger.getLogger( this .getClass()); @Autowired private SchedulerFactoryBean schedulerFactoryBean; /** * 获取所有计划中的任务列表 * * @return * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/getAllJob" ) @ResponseBody @ControllerLogAnnotation (description = "获取所有计划中的任务列表" ) public void getAllJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); Set<JobKey> jobKeys = scheduler.getJobKeys(matcher); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { ScheduleJob job = new ScheduleJob(); job.setJobId(trigger.getDescription()); //description 放的是job的id job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription( "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } } //输出 if (jobList.size() > 0 ){ JSONArray listArray=JSONArray.fromObject(jobList); Map<String,Object> m = new HashMap<String, Object>(); m.put( "job" , listArray); response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\",\"data\":" +m+ "}" ); out.close(); } else { response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"没有计划任务\"}" ); out.close(); } } /** * 所有正在运行的job * * @return * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/getRunningJob" ) @ResponseBody public void getRunningJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { Scheduler scheduler = schedulerFactoryBean.getScheduler(); List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { ScheduleJob job = new ScheduleJob(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); job.setJobName(jobKey.getName()); job.setJobGroup(jobKey.getGroup()); job.setDescription( "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); job.setJobStatus(triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); job.setCronExpression(cronExpression); } jobList.add(job); } //输出 if (jobList.size() > 0 ){ JSONArray listArray=JSONArray.fromObject(jobList); Map<String,Object> m = new HashMap<String, Object>(); m.put( "job" , listArray); response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\",\"data\":" +m+ "}" ); out.close(); } else { response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"没有正在执行的任务\"}" ); out.close(); } } /** * 添加任务 * * @param * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/addJob" ) @ResponseBody public void addJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info= new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); ScheduleJob job = new ScheduleJob(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ job.setJobId(sTimetask.getId()); job.setJobGroup(sTimetask.getGroupName()); //任务组 job.setJobName(sTimetask.getName()); // 任务名称 job.setJobStatus(sTimetask.getJobStatus()); // 任务发布状态 job.setIsConcurrent(sTimetask.getConcurrent()? "1" : "0" ); // 运行状态 job.setCronExpression(sTimetask.getCron()); job.setBeanClass(sTimetask.getBeanName()); // 一个以所给名字注册的bean的实例 job.setMethodName(sTimetask.getMethodName()); job.setJobData(sTimetask.getJobData()); //参数 } } InitQuartzJob.addJob(job); //输入 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } /** * 暂停一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/pauseJob" ) @ResponseBody public void pauseJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info= new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.pauseJob(jobKey); //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } else { //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"任务不存在\"}" ); out.close(); } } } /** * 恢复一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/resumeJob" ) @ResponseBody public void resumeJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info= new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.resumeJob(jobKey); //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } else { //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"任务不存在\"}" ); out.close(); } } } /** * 删除一个job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/deleteJob" ) @ResponseBody public void deleteJob(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info= new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.deleteJob(jobKey); //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } else { //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"任务不存在\"}" ); out.close(); } } } /** * 立即执行job * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/runAJobNow" ) @ResponseBody public void runAJobNow(HttpServletRequest request,HttpServletResponse response) throws SchedulerException, IOException { StringBuffer info= new StringBuffer(); ServletInputStream in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); scheduler.triggerJob(jobKey); //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } else { //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"任务不存在\"}" ); out.close(); } } } /** * 更新job时间表达式 * * @param scheduleJob * @throws SchedulerException * @throws IOException */ @RequestMapping (value= "/updateJobCron" ) @ResponseBody public void updateJobCron(HttpServletRequest request,HttpServletResponse response) throws SchedulerException{ try { StringBuffer info= new StringBuffer(); ServletInputStream in; in = request.getInputStream(); BufferedInputStream buf = new BufferedInputStream(in); byte [] buffer= new byte [ 1024 ]; int iRead; while ((iRead=buf.read(buffer))!=- 1 ){ info.append( new String(buffer, 0 ,iRead, "UTF-8" )); } // 释放资源 buf.close(); in.close(); if (info!= null &&!StringUtil.isEmpty(info.toString())){ JSONObject json = JSONObject.parseObject(info.toString()); STimetask sTimetask = JSONObject.toJavaObject(json, STimetask. class ); if (sTimetask != null ){ Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobKey jobKey = JobKey.jobKey(sTimetask.getName(), sTimetask.getGroupName()); TriggerKey triggerKey = TriggerKey.triggerKey(sTimetask.getName(), sTimetask.getGroupName()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(sTimetask.getCron()); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); scheduler.rescheduleJob(triggerKey, trigger); //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"2000\",\"message\":\"成功\"}" ); out.close(); } else { //输出 response.setHeader( "Content-type" , "text/html;charset=UTF-8" ); response.setCharacterEncoding( "UTF-8" ); PrintWriter out = response.getWriter(); out.write( "{\"code\":\"5000\",\"message\":\"任务不存在\"}" ); out.close(); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
3.编写 定时清除timeTaskLog 7天之前的记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Component public class TimeTaskLogDispatchController { private static final Logger LOGGER = Logger.getLogger(TimeTaskLogDispatchController. class ); /** * 定时清除timeTaskLog 7天之前的记录 */ public void deleteTimeTaskLog(String data) { LOGGER.info( "【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始执行" ); STimetaskLogService bActiveService = (STimetaskLogService) ApplicationContextUtils.getBean(STimetaskLogService. class ); Date currentDate = new Date(); int day = - 7 ; Date deleteDate = DateUtil.getAfterNumDay(currentDate, day); int ret = bActiveService.deleteLog(deleteDate); if (ret > 0 ){ LOGGER.info( "【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>执行成功" ); } else { LOGGER.info( "【定时清除timeTaskLog 7天之前的记录】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>执行失败" ); } } } |
4.dispatcher-servlet.xml中添加如下配置
1 2 3 4 5 6 | <!-- 初始化springUtils --> < bean id="springUtils" class="com.ffxl.quartz.task.util.SpringUtils" /> <!-- 初始化Scheduler --> < bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" /> <!-- 初始化job --> < bean id="initQuartzJob" class="com.ffxl.quartz.init.InitQuartzJob" init-method="init" lazy-init="false" /> |
admin项目部分代码
1.可视化项目的controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | package com.ffxl.admin.controller.task; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpSession; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.ffxl.admin.controller.BaseController; import com.ffxl.admin.util.DataTablesUtil; import com.ffxl.cloud.model.STimetask; import com.ffxl.cloud.model.warpper.ScheduleJob; import com.ffxl.cloud.service.STimetaskService; import com.ffxl.platform.constant.Const; import com.ffxl.platform.core.Page; import com.ffxl.platform.exception.BusinessException; import com.ffxl.platform.util.CronUtil; import com.ffxl.platform.util.DateUtil; import com.ffxl.platform.util.HttpConnectUtil; import com.ffxl.platform.util.JsonDateValueProcessor; import com.ffxl.platform.util.JsonResult; import com.ffxl.platform.util.Message; import com.ffxl.platform.util.StringUtil; /** * 定时任务 * @author wison * */ @Controller @RequestMapping (value = "/task" ) public class TimeTaskController extends BaseController { private static String JOB_URL = Const.QUARTZ_JOB_URL; private static String ALL_JOB = JOB_URL+ "/opt/getAllJob" ; //所有计划中的任务列表 private static String RUNNING_JOB = JOB_URL+ "/opt/getRunningJob" ; //所有正在运行的job private static String ADD_JOB = JOB_URL+ "/opt/addJob" ; //添加任务 private static String PAUSE_JOB =JOB_URL+ "/opt/pauseJob" ; //暂停一个job private static String RESUME_JOB = JOB_URL+ "/opt/resumeJob" ; //恢复一个job private static String DELETE_JOB = JOB_URL+ "/opt/deleteJob" ; //删除一个job private static String RUNA_JOB =JOB_URL+ "/opt/runAJobNow" ; //立即执行job private static String UPDATE_JOB = JOB_URL+ "/opt/updateJobCron" ; //更新job时间表达式 private static final Logger logger = LoggerFactory.getLogger(TimeTaskController. class ); @Autowired private STimetaskService stimetaskService; @InitBinder public void initBinder(WebDataBinder binder) { DateFormat fmt = new SimpleDateFormat(DateUtil.STANDARD_DATE_FORMAT_STR); CustomDateEditor dateEditor = new CustomDateEditor(fmt, true ); binder.registerCustomEditor(Date. class , dateEditor); } /** * 列表页面跳转 * @return */ @RequestMapping (value= "/list" ) public ModelAndView userList(STimetask task){ ModelAndView mv = this .getModelAndView(); mv.setViewName( "system/timeTaskList" ); return mv; } /** * 列表 * @return */ @RequestMapping (value= "/task_list" ) @ResponseBody public JsonResult taskList(DataTablesUtil dataTables, STimetask task, Page page, HttpSession session){ List<STimetask> list = stimetaskService.selectByPage(task, page); // 查询task的运行状态 String result = HttpConnectUtil.httpRequest(RUNNING_JOB, Const.REQUEST_METHOD_POST, null ); if (result!= null ){ JSONObject jsonResult = JSONObject.fromObject(result); Map<String, ScheduleJob> map = new HashMap<String, ScheduleJob>(); if ( jsonResult.get( "code" ).equals( "2000" )){ JSONObject js = (JSONObject) jsonResult.get( "data" ); JSONArray dataArray = (JSONArray) js.get( "job" ); if (dataArray.size() > 0 ){ List<ScheduleJob> jobList = JSONArray.toList(dataArray,ScheduleJob. class ); for (ScheduleJob job: jobList){ map.put(job.getJobId().toString(), job); } } } for (STimetask st: list){ if (map.containsKey(st.getId())){ st.setConcurrent( true ); } } } // 查询task的计划状态 String planResult = HttpConnectUtil.httpRequest(ALL_JOB, Const.REQUEST_METHOD_POST, null ); if (planResult!= null ){ JSONObject jsonPlanResult = JSONObject.fromObject(planResult); Map<String, ScheduleJob> planMap = new HashMap<String, ScheduleJob>(); if (jsonPlanResult.get( "code" ).equals( "2000" )){ JSONObject js = (JSONObject) jsonPlanResult.get( "data" ); JSONArray dataArray = (JSONArray) js.get( "job" ); if (dataArray.size() > 0 ){ List<ScheduleJob> jobList = JSONArray.toList(dataArray,ScheduleJob. class ); for (ScheduleJob job: jobList){ planMap.put(job.getJobId().toString(), job); } } } for (STimetask st: list){ if (planMap.containsKey(st.getId())){ String status = planMap.get(st.getId()).getJobStatus(); st.setPlanStatus(status); } } } //返回dataTable所需数据 dataTables = this .getDataTables(page, dataTables, list); return new JsonResult( "2000" , dataTables); } /** * 立即执行一次job * 用于测试任务是否正确 * @param id * @return */ @RequestMapping (value= "/run_task2job" ) @ResponseBody public JsonResult run_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date. class , new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(RUNA_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if (result == null ){ return new JsonResult( "5000" , "定时项目未启动" , null ); } else { return new JsonResult( "2000" , null ); } } /** * 添加job到计划列表 * @param id * @return */ @RequestMapping (value= "/add_task2job" ) @ResponseBody public JsonResult add_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date. class , new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(ADD_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if (result == null ){ return new JsonResult( "5000" , "定时项目未启动" , null ); } else { return new JsonResult( "2000" , null ); } } /** * 从计划列表中暂停job * @param id * @return */ @RequestMapping (value= "/stop_task2job" ) @ResponseBody public JsonResult stop_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date. class , new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(PAUSE_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if (result == null ){ return new JsonResult( "5000" , "定时项目未启动" , null ); } else { return new JsonResult( "2000" , null ); } } /** * 从计划列表中移除job * @param id * @return */ @RequestMapping (value= "/remove_task2job" ) @ResponseBody public JsonResult remove_task2job(String id){ //查询task STimetask stimetask = stimetaskService.selectByPrimaryKey(id); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date. class , new JsonDateValueProcessor()); JSONObject jsonArray = JSONObject.fromObject(stimetask,jsonConfig); String result = HttpConnectUtil.httpRequest(DELETE_JOB, Const.REQUEST_METHOD_POST, jsonArray.toString()); logger.info(result); if (result == null ){ return new JsonResult( "5000" , "定时项目未启动" , null ); } else { return new JsonResult( "2000" , null ); } } /** * 变更job状态 * @param id * @return */ @RequestMapping (value= "/update_task" ) @ResponseBody public JsonResult update_task(String ids,String type){ //查询task String[] idArray = ids.split( "," ); Map<String,String> selectedIdMap = new HashMap<String,String>(); List<String> idList = new ArrayList<String>(); for ( int i = 0 ; i < idArray.length; i++) { idList.add(idArray[i]); } int ret = stimetaskService.updatebyOperate(idList,type); if (ret > 0 ){ return new JsonResult( true ); } else { return new JsonResult( false ); } } /** * 删除job * @param id * @return */ @RequestMapping (value= "/delete_task" ) @ResponseBody public JsonResult delete_task(String ids){ //查询task String[] idArray = ids.split( "," ); Map<String,String> selectedIdMap = new HashMap<String,String>(); List<String> idList = new ArrayList<String>(); for ( int i = 0 ; i < idArray.length; i++) { idList.add(idArray[i]); } int ret = stimetaskService.deleteByIds(idList); if (ret > 0 ){ return new JsonResult( true ); } else { return new JsonResult( false ); } } /** * 详情页面 * @return */ @RequestMapping (value= "/task_detail" ) public ModelAndView detail(String id){ ModelAndView mv = this .getModelAndView(); STimetask model = new STimetask(); model = stimetaskService.selectByPrimaryKey(id); mv.addObject( "model" , model); mv.setViewName( "system/timeTaskDetail" ); return mv; } /** * 解析cron * @return */ @RequestMapping (value= "/analysis_cron" ) @ResponseBody public JsonResult analysisCron(String cron){ try { Date date = new Date(); String dateStr = DateUtil.formatStandardDatetime(date); List<String> dateList = CronUtil.cronAlgBuNums(cron, dateStr, 5 ); return new JsonResult( "2000" , dateList); } catch (Exception e) { e.printStackTrace(); return new JsonResult( "5000" , null ); } } /** * 验证名称是否存在 * @param id * @param groupName * @param name * @return * */ @RequestMapping (value= "/check_name" ) @ResponseBody public Boolean check_name(String id, String groupName, String name){ if (StringUtil.isEmpty(groupName,name)){ throw new BusinessException(Message.M4003); } STimetask task = new STimetask(); task.setId(id); task.setGroupName(groupName); task.setName(name); STimetask queryTask = stimetaskService.checkName(task); if (queryTask != null ){ logger.debug( "组.任务名 exists,return false" ); return false ; } else { logger.debug( "组.任务名 not exists,return true" ); return true ; } } /** * 保存 * @return */ @RequestMapping (value= "/task_save" ) @ResponseBody public JsonResult userSave(STimetask task, HttpSession session){ //获取系统操作人员 String longName = "admin" ; task.setModifyUserId(longName); try { int ret= stimetaskService.insertOrUpdateByUser(task,longName); if (ret > 0 ){ return new JsonResult( "2000" ,task); } else { return new JsonResult( "5000" ); } } catch (BusinessException e){ return new JsonResult( "5001" ,e.getMessage(), null ); } } } |
2.前端页面
可以看到在这里我把定时任务的状态分为两大类,任务状态跟业务有关,分为已发布和未发布;计划状态跟定时任务的运行有关,分为None,正常运行,已暂停,任务执行中,线程阻塞,未计划,错误
2.1计划状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function formatPlan(value, rowData, rowIndex){ if (value== "None" ){ return "<span class='text-danger'>None</span>" } if (value== "NORMAL" ){ return "<span class='text-success'>正常运行</span>" } if (value== "PAUSED" ){ return "<span class='text-yellow'>已暂停</span>" } if (value== "COMPLETE" ){ return "<span class='text-success'>任务执行中</span>" } if (value== "BLOCKED" ){ return "<span class='text-danger'>线程阻塞</span>" } if (value== "ERROR" ){ return "<span class='text-danger'>错误</span>" } else { return "<span class='text-danger'>未计划</span>" } } |
2.2操作逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | function formatOpt(value, rowData, rowIndex) { var msg = "" ; msg+= "<a href='#' class='btnstyle' onclick='showDetail(\"" + rowData.id + "\")'>编辑</a>" ; //已发布, if (rowData.jobStatus== '1' ){ var value = rowData.planStatus; if (value== "None" || value== null ){ msg += '<a href="#"class="btnstyle" onclick="addJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="定时任务按照计划开始执行" >计划</a>' ; } if (value== "NORMAL" ){ msg += '<a href="#"class="btnstyle" onclick="runJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="紧执行一次" >立即执行</a>' + '<a href="#"class="btnstyle" onclick="stopJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="定时任务暂时停止执行" >暂停</a>' + '<a href="#"class="btnstyle" onclick="removeJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="定时任务从计划列表中移除" >移除</a>' ; } if (value== "PAUSED" ){ msg += '<a href="#"class="btnstyle" onclick="runJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="紧执行一次" >立即执行</a>' + '<a href="#"class="btnstyle" onclick="addJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="定时任务按照计划开始执行" >计划</a>' + '<a href="#"class="btnstyle" onclick="removeJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="定时任务从计划列表中移除" >移除</a>' ; } if (value== "COMPLETE" ){ msg += '<a href="#"class="btnstyle" onclick="runJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="紧执行一次" >立即执行</a>' } if (value== "BLOCKED" ){ msg += '<a href="#"class="btnstyle" onclick="runJob(\'' +rowData.id+ '\')" ' + 'onMouseOver="popTip(this,\' ' +rowData+ ' \' )" class="btn btn-info btn-xs" data-toggle="tooltip"' + ' data-placement="top" title="紧执行一次" >立即执行</a>' } if (value== "ERROR" ){ } } return msg; } |
简单概述为:已发布的定时任务出现【计划】按钮;执行【计划】后,定时任务正常运行,且出现【立即执行】【暂停】【移除】三个按钮,【立即执行】用于测试当前定时任务的业务逻辑是否正确,【暂停】很容易理解,就是把当前任务暂停,及时到达cron定义的时间,定时任务也不会执行,【移除】仅仅是把计划列表中相应的任务暂时清除掉,你可以理解为清除缓存中的定时任务,并不是物理删除
3.定时任务详情
3.点击规则弹出页面如下,可自行修改界面,这里我是从网上随便找了个插件,然后按照自己想要的修改下源码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?