Spring+Quartz实现动态添加定时任务

发布时间:2018-12-03
技术:spring4.0.2+quartz2.2.1

概述

在最近工作中,由于涉及到定时任务特别多,而这些工作又是由下属去完成的,在生成环境中经常会出现业务逻辑错误,分析下来多数是定时任务运行问题,所以就希望把定时任务优化一下,主要实现2个方面 1.定时任务动态配置及持久化 2.可视化的管理界面,可以非常清晰的管理自己的所有定时任务 源码是我重新梳理后的(陆陆续续花了我好几天晚上),整个框架去除了多余的内容,仅保留quartz及springAop,能非常好的解决业务中的定时任务,而且还能所见即所得的知道目前有哪些任务在跑,对任务具体执行的情况进行日志分析;框架采用ssm搭建,结合自己工作中框架的结构问题做了优化,如果对这个单体架构感兴趣,底层可以在platform_parent中扩展,应用层可在cloud_parent中进行扩展

详细

一、准备工作

1.java环境搭建,具体参考包中的webapp/resources/doc/平台开发环境安装Guide_V1.0.docx文档

image.png

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  运行结果如图:

image.png

二、代码引入

1、文件引入顺序:lib_parent  platform_parent  cloud_parent

2、代码结构

image.png

三、程序实现

quartz项目部分代码

1.quartz项目启动时,初始化数据库中的定时任务

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对应的操作服务

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天之前的记录

@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中添加如下配置

<!-- 初始化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层

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.前端页面

1.png

可以看到在这里我把定时任务的状态分为两大类,任务状态跟业务有关,分为已发布和未发布;计划状态跟定时任务的运行有关,分为None,正常运行,已暂停,任务执行中,线程阻塞,未计划,错误

2.1计划状态

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操作逻辑

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.定时任务详情

2.png

3.点击规则弹出页面如下,可自行修改界面,这里我是从网上随便找了个插件,然后按照自己想要的修改下源码

3.png


本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
手机上随时阅读、收藏该文章 ?请扫下方二维码