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

时间:2021-08-05 16:43:34
发布时间: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文档

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

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

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

二、代码引入

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

2、代码结构

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

三、程序实现

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

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

可以看到在这里我把定时任务的状态分为两大类,任务状态跟业务有关,分为已发布和未发布;计划状态跟定时任务的运行有关,分为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.定时任务详情

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

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

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

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权