当前位置:网站首页>SpingBoot整合Quartz框架实现动态定时任务(支持实时增删改查任务)
SpingBoot整合Quartz框架实现动态定时任务(支持实时增删改查任务)
2022-07-29 06:38:00 【@素素~】
SpingBoot整合Quartz框架实现动态定时任务(支持实时增删改查任务)
1.前言
上篇动态定时任务文章
SpringBoot动态定时任务的动态配置处理(动态实时读取数据库配置,支持不重启服务下动态修改和新增任务).这种方式实现起来是简单,但是会有好多弊端,自己可以网上搜搜看看,也许是自己还没搞明白,所以觉得不好用,所以推荐大家使用今天要介绍的Quartz框架,当然根据自己所需进行选择
2. 关于Quartz入门讲解推荐
推荐一篇很不错的文章,大家可以看看,本文也就不介绍了,待会儿直接上案例,想了解详细一点的,可以看看下面文章,对刚接触的童鞋还是非常友好的讲解。
3. 关于参考代码地址请进
- 经朋友分享clone了下面地址的代码,发现里面刚好有介绍Quartz,本来一直都想弄个关于Quartz的demo,一直没弄,看完之后觉得有必要弄个demo演示一下,推荐大家clone大佬上传到gitHub上的代码,如果你不clone直接看我的demo也行,我下面的就是做了改动,初始化表也少了些
https://github.com/xkcoding/spring-boot-demo.
4. 先看效果
- 新增任务

- 查询、修改状态、删除等

5. 准备工作
5.1 数据库
- ① 初始化下面几张表:qrtz,创表即可,没有数据,创表语句后面贴,本来11张表,我这里用到这几个,所以建全部表的话,可以参考上面github上的

- 在页面新增任务之后会自动填充这几张表的数据,当然你也可以数据库里直接insert,就是麻烦一点,先截图简单看一下这几个表的数据结构


dog_play_plan这个表是根据业务逻辑自己创建,后面介绍
5.2 Java后端配置等
5.2.1 pom 文件
- 如下:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
5.2.2 yml 配置文件
- 如下:

spring: quartz: # 参见 org.springframework.boot.autoconfigure.quartz.QuartzProperties job-store-type: jdbc wait-for-jobs-to-complete-on-shutdown: true scheduler-name: SpringBootDemoScheduler properties: org.quartz.threadPool.threadCount: 5 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true org.quartz.jobStore.misfireThreshold: 5000 org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 在调度流程的第一步,也就是拉取待即将触发的triggers时,是上锁的状态,即不会同时存在多个线程拉取到相同的trigger的情况,也就避免的重复调度的危险。参考:https://segmentfault.com/a/1190000015492260 org.quartz.jobStore.acquireTriggersWithinLock: true # org.quartz.jobStore.tablePrefix: sys_
6. 核心代码(直接粘代码的往下)
6.1 vo、mapper 与 mapper.xml
- 如图:



6.2 controller、service
- 先截一个新增,其他的下面贴代码



6.3 JobUtil.java
- 如图:

6.4 BaseJob、定时 job(HelloJob为例)
- 如图:


6.5 job.html
- 如图:

6.6 测试看效果
- ① 启动服务,新增任务,查看新增的任务




- ② 停用任务


如果再启用,点恢复即可,傻瓜操作不介绍了,主要还是代码实现
7. 实现一个业务场景
7.1 dog_play_plan——计划表
- 只说一下表就行了,实体和mapper和xml略了


7.2 DogPlayPlanJob.java
- 如下:

当然你也可以直接处理业务,不弄计划表,看自己情况,为啥弄个计划表,打个比方要发短信,SendMessageJob有一个,但是有的2分钟发一次有的5分钟发一次,所以你可以弄成同一个组sendMessage但是不同的任务名2分钟一次和5分钟一次,但是你需要处理一下哪些业务数据是2分钟一次,哪些要5分钟一次,当然我这计划表可能不是更合理的,所以需要根据自己需要优化
7.3 测试
- 启动服务,添加业务,测试如下:


7.3 后期可优化点
1. 控制添加任务的数据
- 新增任务不能随意,所以可以将业务名称、全类名维护字典或枚举或者直接将表单数据维护到一张表里,勾选数据进行反显,确保数据的正确性,尤其是全类名或cron表达式
2. dog_play_plan 表
- 这个表根据自己需求情况进行优化,我这里只是简单打个比方
3. 定时任务管理应做成权限控制
- 这个功能相对来说还是比较重要的,可以页面控制,但是要弄成权限控制,不能随意增删改查
- 好了,简单说这么多,自己看着优化即可
7.4 对上面7.3提的进行一个简单的优化
1. 用枚举维护jobGroupName 和 jobName
- 为了避免新增任务时数据乱写,所以维护起来

2. 新增 sys_job_manager 表——用来管理定时任务新增用
- 表结构如下:


- 用途:
此表后台可以开发者维护,页面新增任务的时候,取此表维护的数据,如果没有维护不允许乱新增任务
3. 控制添加任务的查询sql
- 查询所有可添加的任务:

- 检查添加的任务是否维护

4. controller修改如下
- 如下

5. 新增时,注意数据
- 如下:

6. 别忘了 dog_play_plan
- 这个表看情况,没有对应的计划也可不维护,但是要先维护上面的任务,再新增这个计划

这个表,看自己需求情况进行优化,大概意思是这个意思
8.附代码
8.1 数据库建表
- 按顺序如下:
CREATE TABLE `qrtz_cron_triggers` ( `SCHED_NAME` varchar(120) NOT NULL, `TRIGGER_NAME` varchar(200) NOT NULL, `TRIGGER_GROUP` varchar(200) NOT NULL, `CRON_EXPRESSION` varchar(200) NOT NULL, `TIME_ZONE_ID` varchar(80) DEFAULT NULL, PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `qrtz_fired_triggers` ( `SCHED_NAME` varchar(120) NOT NULL, `ENTRY_ID` varchar(95) NOT NULL, `TRIGGER_NAME` varchar(200) NOT NULL, `TRIGGER_GROUP` varchar(200) NOT NULL, `INSTANCE_NAME` varchar(200) NOT NULL, `FIRED_TIME` bigint(20) NOT NULL, `SCHED_TIME` bigint(20) NOT NULL, `PRIORITY` int(11) NOT NULL, `STATE` varchar(16) NOT NULL, `JOB_NAME` varchar(200) DEFAULT NULL, `JOB_GROUP` varchar(200) DEFAULT NULL, `IS_NONCONCURRENT` bit(1) DEFAULT NULL, `REQUESTS_RECOVERY` bit(1) DEFAULT NULL, PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `qrtz_job_details` ( `SCHED_NAME` varchar(120) NOT NULL, `JOB_NAME` varchar(200) NOT NULL, `JOB_GROUP` varchar(200) NOT NULL, `DESCRIPTION` varchar(250) DEFAULT NULL, `JOB_CLASS_NAME` varchar(250) NOT NULL, `IS_DURABLE` bit(1) NOT NULL, `IS_NONCONCURRENT` bit(1) NOT NULL, `IS_UPDATE_DATA` bit(1) NOT NULL, `REQUESTS_RECOVERY` bit(1) NOT NULL, `JOB_DATA` blob, PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `qrtz_locks` ( `SCHED_NAME` varchar(120) NOT NULL, `LOCK_NAME` varchar(40) NOT NULL, PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `qrtz_paused_trigger_grps` ( `SCHED_NAME` varchar(120) NOT NULL, `TRIGGER_GROUP` varchar(200) NOT NULL, PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='不加启动报错'; CREATE TABLE `qrtz_triggers` ( `SCHED_NAME` varchar(120) NOT NULL, `TRIGGER_NAME` varchar(200) NOT NULL, `TRIGGER_GROUP` varchar(200) NOT NULL, `JOB_NAME` varchar(200) NOT NULL, `JOB_GROUP` varchar(200) NOT NULL, `DESCRIPTION` varchar(250) DEFAULT NULL, `NEXT_FIRE_TIME` bigint(20) DEFAULT NULL, `PREV_FIRE_TIME` bigint(20) DEFAULT NULL, `PRIORITY` int(11) DEFAULT NULL, `TRIGGER_STATE` varchar(16) NOT NULL, `TRIGGER_TYPE` varchar(8) NOT NULL, `START_TIME` bigint(20) NOT NULL, `END_TIME` bigint(20) DEFAULT NULL, `CALENDAR_NAME` varchar(200) DEFAULT NULL, `MISFIRE_INSTR` smallint(6) DEFAULT NULL, `JOB_DATA` blob, PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `qrtz_simple_triggers` ( `SCHED_NAME` varchar(120) NOT NULL, `TRIGGER_NAME` varchar(200) NOT NULL, `TRIGGER_GROUP` varchar(200) NOT NULL, `REPEAT_COUNT` bigint(20) NOT NULL, `REPEAT_INTERVAL` bigint(20) NOT NULL, `TIMES_TRIGGERED` bigint(20) NOT NULL, PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='修改定时任务时用这个';
8.2 Java核心代码
1. vo、mapper 与 mapper.xml 等
- ① JobForm.java
package com.liu.susu.task.quartz.vobo.form; import lombok.Data; import lombok.experimental.Accessors; import javax.validation.constraints.NotBlank; @Data @Accessors(chain = true) public class JobForm { /** * 定时任务名 */ @NotBlank(message = "任务名称") private String jobName; /** * 任务组名 */ @NotBlank(message = "任务组名不能为空") private String jobGroupName; /** * 定时任务全类名 */ @NotBlank(message = "定时任务全类名") private String jobClassName; /** * 定时任务cron表达式 */ @NotBlank(message = "cron表达式不能为空") private String cronExpression; } - ② JobAndTriggerVo.java
package com.liu.susu.task.quartz.vobo.vo; import lombok.Data; import java.math.BigInteger; @Data public class JobAndTriggerVo { /** * 定时任务名称 */ private String jobName; /** * 定时任务组 */ private String jobGroup; /** * 定时任务全类名 */ private String jobClassName; /** * 触发器名称 */ private String triggerName; /** * 触发器组 */ private String triggerGroup; /** * 重复间隔 */ private BigInteger repeatInterval; /** * 触发次数 */ private BigInteger timesTriggered; /** * cron 表达式 */ private String cronExpression; /** * 时区 */ private String timeZoneId; /** * 定时任务状态 */ private String triggerState; } - ③ JobMapper.java
package com.liu.susu.mapper.task; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import java.util.List; @Mapper @Repository public interface JobMapper { /** * 查询定时作业和触发器列表 */ List<JobAndTriggerVo> list(); } - ④ JobMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.liu.susu.mapper.task.JobMapper"> <select id="list" resultType="com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo"> SELECT job_details.`JOB_NAME`, job_details.`JOB_GROUP`, job_details.`JOB_CLASS_NAME`, cron_triggers.`CRON_EXPRESSION`, cron_triggers.`TIME_ZONE_ID`, qrtz_triggers.`TRIGGER_NAME`, qrtz_triggers.`TRIGGER_GROUP`, qrtz_triggers.`TRIGGER_STATE` FROM `QRTZ_JOB_DETAILS` job_details LEFT JOIN `QRTZ_CRON_TRIGGERS` cron_triggers ON job_details.`JOB_NAME` = cron_triggers.`TRIGGER_NAME` AND job_details.`JOB_GROUP` = cron_triggers.`TRIGGER_GROUP` LEFT JOIN `QRTZ_TRIGGERS` qrtz_triggers ON qrtz_triggers.`TRIGGER_NAME` = job_details.`JOB_NAME` AND qrtz_triggers.`TRIGGER_GROUP` = job_details.`JOB_GROUP` </select> </mapper>
2. controller、service
- ① JobController.java
package com.liu.susu.controller.task; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.github.pagehelper.PageInfo; import com.liu.susu.common.ResultData; import com.liu.susu.common.ReturnCode; import com.liu.susu.service.task.JobService; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import com.liu.susu.task.quartz.vobo.form.JobForm; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @RestController @RequestMapping("/job") @Slf4j public class JobController { private final JobService jobService; @Autowired public JobController(JobService jobService) { this.jobService = jobService; } /** * 保存定时任务 */ @PostMapping public ResultData addJob(@Valid JobForm form) { try { jobService.addJob(form); return new ResultData(ReturnCode.INSERT_SUCCESS); } catch (Exception e) { return new ResultData(ReturnCode.FAIL_999999); } } /** * 暂停定时任务 */ @PutMapping(params = "pause") public ResultData pauseJob(JobForm form) throws SchedulerException { if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) { return new ResultData(ReturnCode.FAIL_NO_PARAM, "参数不能为空!"); } jobService.pauseJob(form); return new ResultData(ReturnCode.SUCCESS_000000, "暂停成功!"); } /** * 恢复定时任务 */ @PutMapping(params = "resume") public ResultData resumeJob(JobForm form) throws SchedulerException { if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) { return new ResultData(ReturnCode.FAIL_NO_PARAM, "参数不能为空!"); } jobService.resumeJob(form); return new ResultData(ReturnCode.SUCCESS_000000, "恢复成功!"); } /** * 修改定时任务,定时时间 */ @PutMapping(params = "cron") public ResultData cronJob(@Valid JobForm form) { try { jobService.cronJob(form); return new ResultData(ReturnCode.UPDATE_SUCCESS, "修改成功!"); } catch (Exception e) { return new ResultData(ReturnCode.FAIL_999999); } } /** * 删除定时任务 */ @DeleteMapping public ResultData deleteJob(JobForm form) throws SchedulerException { if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) { return new ResultData(ReturnCode.FAIL_NO_PARAM, "参数不能为空!"); } jobService.deleteJob(form); return new ResultData(ReturnCode.DELETE_SUCCESS, "删除成功!"); } /** * 查询定时任务列表 * @param currentPage * @param pageSize * @return */ @RequestMapping public ResultData jobList(Integer currentPage, Integer pageSize) { if (ObjectUtil.isNull(currentPage)) { currentPage = 1; } if (ObjectUtil.isNull(pageSize)) { pageSize = 10; } PageInfo<JobAndTriggerVo> all = jobService.list(currentPage, pageSize); return new ResultData(ReturnCode.SELECT_SUCCESS, all); } } - ② JobService.java
package com.liu.susu.service.task; import com.github.pagehelper.PageInfo; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import com.liu.susu.task.quartz.vobo.form.JobForm; import org.quartz.SchedulerException; public interface JobService { /** * 添加并启动定时任务 */ void addJob(JobForm form) throws Exception; /** * 删除定时任务 */ void deleteJob(JobForm form) throws SchedulerException; /** * 暂停定时任务 */ void pauseJob(JobForm form) throws SchedulerException; /** * 恢复定时任务 */ void resumeJob(JobForm form) throws SchedulerException; /** * 重新配置定时任务 */ void cronJob(JobForm form) throws Exception; /** * 查询定时任务列表 */ PageInfo<JobAndTriggerVo> list(Integer currentPage, Integer pageSize); } - ③ JobServiceImpl.java
package com.liu.susu.service.task.impl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.liu.susu.mapper.task.JobMapper; import com.liu.susu.service.task.JobService; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import com.liu.susu.task.quartz.vobo.form.JobForm; import com.liu.susu.utils.JobUtil; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service @Slf4j public class JobServiceImpl implements JobService { private final Scheduler scheduler; private final JobMapper jobMapper; @Autowired public JobServiceImpl(Scheduler scheduler, JobMapper jobMapper) { this.scheduler = scheduler; this.jobMapper = jobMapper; } /** * 添加并启动定时任务 */ @Override public void addJob(JobForm form) throws Exception { // 启动调度器 scheduler.start(); // 构建Job信息 // JobDetail jobDetail = JobBuilder.newJob(JobUtil.getClass(form.getJobClassName()).getClass()).withIdentity(form.getJobClassName(), form.getJobGroupName()).build(); JobDetail jobDetail = JobBuilder.newJob(JobUtil.getClass(form.getJobClassName()).getClass()).withIdentity(form.getJobName(), form.getJobGroupName()).build(); // Cron表达式调度构建器(即任务执行的时间) CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(form.getCronExpression()); //根据Cron表达式构建一个Trigger // CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(form.getJobClassName(), form.getJobGroupName()).withSchedule(cron).build(); CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(form.getJobName(), form.getJobGroupName()).withSchedule(cron).build(); try { scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { log.error("【定时任务】创建失败!", e); throw new Exception("【定时任务】创建失败!"); } } /** * 删除定时任务 */ @Override public void deleteJob(JobForm form) throws SchedulerException { scheduler.pauseTrigger(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName())); scheduler.unscheduleJob(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName())); scheduler.deleteJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName())); } /** * 暂停定时任务 */ @Override public void pauseJob(JobForm form) throws SchedulerException { scheduler.pauseJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName())); } /** * 恢复定时任务 */ @Override public void resumeJob(JobForm form) throws SchedulerException { scheduler.resumeJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName())); } /** * 重新配置定时任务 */ @Override public void cronJob(JobForm form) throws Exception { try { TriggerKey triggerKey = TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName()); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(form.getCronExpression()); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 根据Cron表达式构建一个Trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { log.error("【定时任务】更新失败!", e); throw new Exception("【定时任务】创建失败!"); } } /** * 查询定时任务列表 */ @Override public PageInfo<JobAndTriggerVo> list(Integer currentPage, Integer pageSize) { PageHelper.startPage(currentPage, pageSize); List<JobAndTriggerVo> list = jobMapper.list(); return new PageInfo<>(list); } }
3. JobUtil.java
- 如下:
package com.liu.susu.utils; import com.liu.susu.task.quartz.job.base.BaseJob; /** * 定时任务反射工具类 */ public class JobUtil { /** * 根据全类名获取Job实例 */ public static BaseJob getClass(String classname) throws Exception { Class<?> clazz = Class.forName(classname); return (BaseJob) clazz.newInstance(); } }
4. BaseJob、HelloJob
- ① BaseJob.java
package com.liu.susu.task.quartz.job.base; import org.quartz.*; public interface BaseJob extends Job { @Override void execute(JobExecutionContext context) throws JobExecutionException; } - ② HelloJob.java
package com.liu.susu.task.quartz.job; import com.liu.susu.task.quartz.job.base.BaseJob; import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; import org.quartz.JobKey; import java.time.LocalDateTime; @Slf4j public class HelloJob implements BaseJob { @Override public void execute(JobExecutionContext context) { JobKey key = context.getJobDetail().getKey(); if ("bb".equals(key.getName())){ log.info("处理bb定时任务的业务…… 执行时间-->: {}", LocalDateTime.now()); } if ("aa".equals(key.getName())){ log.info("处理aa定时任务的业务…… 执行时间-->: {}",LocalDateTime.now()); } log.info("处理aa、bb外的定时任务,执行时间-->: {}",LocalDateTime.now()); } }
5. job.html
- 如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>spring-boot-demo-task-quartz</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.9/theme-chalk/index.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script> <script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.9/index.js"></script> <style> #top { /*background: #20A0FF;*/ padding: 5px; /*overflow: hidden*/ } </style> </head> <body> <div id="job"> <div id="top"> <el-button size="small" type="primary" plain @click="search" :loading="loading" icon="el-icon-search">查询 </el-button> <el-button size="small" type="primary" plain @click="handleadd" icon="el-icon-plus">添加</el-button> </div> <br/> <div> <el-table ref="jobTable" :data="tableData" style="width:100%" border center> <el-table-column prop="jobName" label="任务名称" show-overflow-tooltip align="center"></el-table-column> <el-table-column prop="jobGroup" label="任务所在组" sortable align="center"></el-table-column> <el-table-column prop="jobClassName" label="任务类名" align="center"></el-table-column> <el-table-column prop="triggerName" label="触发器名称" align="center"></el-table-column> <el-table-column prop="triggerGroup" label="触发器所在组" sortable align="center"></el-table-column> <el-table-column prop="cronExpression" label="表达式" align="center"></el-table-column> <el-table-column prop="timeZoneId" label="时区" align="center"></el-table-column> <el-table-column prop="triggerState" label="状态" align="center" :formatter="formatState"></el-table-column> <el-table-column label="操作" width="300" align="center"> <template scope="scope"> <el-button size="small" type="warning" @click="handlePause(scope.$index, scope.row)"> 暂停 </el-button> <el-button size="small" type="info" @click="handleResume(scope.$index, scope.row)"> 恢复 </el-button> <el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)"> 删除 </el-button> <el-button size="small" type="success" @click="handleUpdate(scope.$index, scope.row)"> 修改 </el-button> </template> </el-table-column> </el-table> <div align="center"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 20, 30, 40]" :page-size="pagesize" layout="total, sizes, prev, pager, next, jumper" :total="totalCount"> </el-pagination> </div> </div> <el-dialog title="添加任务" :visible.sync="dialogFormVisible"> <el-form :model="form"> <el-form-item label="任务名称" label-width="100px" style="width:90%"> <el-input v-model="form.jobName" auto-complete="off"></el-input> </el-form-item> <el-form-item label="任务类名称" label-width="100px" style="width:90%"> <el-input v-model="form.jobClassName" auto-complete="off"></el-input> </el-form-item> <el-form-item label="任务分组" label-width="100px" style="width:90%"> <el-input v-model="form.jobGroup" auto-complete="off"></el-input> </el-form-item> <el-form-item label="表达式" label-width="100px" style="width:90%"> <el-input v-model="form.cronExpression" auto-complete="off"></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="add">确 定</el-button> </div> </el-dialog> <el-dialog title="修改任务" :visible.sync="updateFormVisible"> <el-form :model="updateform"> <el-form-item label="表达式" label-width="100px" style="width:90%"> <el-input v-model="updateform.cronExpression" auto-complete="off"></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="updateFormVisible = false">取 消</el-button> <el-button type="primary" @click="update">确 定</el-button> </div> </el-dialog> </div> <footer align="center"> <p>© Quartz 定时任务管理</p> </footer> <script> var vue = new Vue({ el: "#job", data: { //表格当前页数据 tableData: [], //请求的URL url: 'job', //默认每页数据量 pagesize: 10, //当前页码 currentPage: 1, //查询的页码 start: 1, //默认数据总数 totalCount: 1000, //添加对话框默认可见性 dialogFormVisible: false, //修改对话框默认可见性 updateFormVisible: false, //提交的表单 form: { jobName: '', jobGroup: '', cronExpression: '' }, updateform: { jobName: '', jobGroup: '', cronExpression: '' }, loading: false }, methods: { // 格式化状态 formatState: function (row, column, cellValue, index) { if (row.triggerState === 'WAITING' || row.triggerState === 'ACQUIRED') { return "运行中"; } else if (row.triggerState === 'PAUSED') { return "暂停"; } else { return "未知状态"; } }, // 从服务器读取数据 loadData: function (currentPage, pageSize) { this.loading = true; this.$http.get('job?' + 'currentPage=' + currentPage + '&pageSize=' + pageSize).then(function (res) { console.log(res); this.tableData = res.body.data.list; this.totalCount = res.body.data.total; this.loading = false; }, function () { console.log('failed'); }); }, // 删除任务 handleDelete: function (index, row) { this.$http.delete('job', { params: { "jobClassName": row.jobName, "jobGroupName": row.jobGroup } }, { emulateJSON: true}).then(function (res) { this.loadData(this.currentPage, this.pagesize); }, function () { console.log('failed'); }); }, // 暂停任务 handlePause: function (index, row) { this.$http.put('job?pause', { "jobClassName": row.jobName, "jobGroupName": row.jobGroup }, { emulateJSON: true}).then(function (res) { this.loadData(this.currentPage, this.pagesize); }, function () { console.log('failed'); }); }, // 恢复任务 handleResume: function (index, row) { this.$http.put('job?resume', { "jobClassName": row.jobName, "jobGroupName": row.jobGroup }, { emulateJSON: true}).then(function (res) { this.loadData(this.currentPage, this.pagesize); }, function () { console.log('failed'); }); }, // 搜索 search: function () { this.loadData(this.currentPage, this.pagesize); }, // 弹出对话框 handleadd: function () { this.dialogFormVisible = true; }, // 添加 add: function () { this.$http.post('job', { "jobName": this.form.jobName, "jobClassName": this.form.jobClassName, "jobGroupName": this.form.jobGroup, "cronExpression": this.form.cronExpression }, { emulateJSON: true}).then(function (res) { this.loadData(this.currentPage, this.pagesize); this.dialogFormVisible = false; }, function () { console.log('failed'); }); }, // 更新 handleUpdate: function (index, row) { console.log(row); this.updateFormVisible = true; this.updateform.jobName = row.jobName; this.updateform.jobGroup = row.jobGroup; }, // 更新任务 update: function () { this.$http.put('job?cron', { "jobClassName": this.updateform.jobName, "jobGroupName": this.updateform.jobGroup, "cronExpression": this.updateform.cronExpression }, { emulateJSON: true} ).then(function (res) { this.loadData(this.currentPage, this.pagesize); this.updateFormVisible = false; }, function () { console.log('failed'); }); }, // 每页显示数据量变更 handleSizeChange: function (val) { this.pagesize = val; this.loadData(this.currentPage, this.pagesize); }, // 页码变更 handleCurrentChange: function (val) { this.currentPage = val; this.loadData(this.currentPage, this.pagesize); } } }); //载入数据 vue.loadData(vue.currentPage, vue.pagesize); </script> </body> </html>
8.3 对于 7.4 优化后的代码 和 下载地址
8.3.1 优化后的代码
1. JobService.java
- 如下:
package com.liu.susu.service.task; import com.liu.susu.common.ResultData; import com.liu.susu.pojo.entity.sys.SysJobManagerEntity; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import java.util.List; public interface JobService { /** * 查询定时任务列表 */ List<JobAndTriggerVo> selectAllJobList(); /** * 添加并启动定时任务 */ void addJob(SysJobManagerEntity form) throws Exception; /** * 暂停定时任务 */ ResultData pauseJob(String jobNameKey, String jobGroupNameCode); /** * 恢复定时任务 */ ResultData resumeJob(String jobNameKey, String jobGroupNameCode) ; /** * 重新配置定时任务 */ ResultData updateJobCron(String jobNameKey, String jobGroupNameCode,String newCron) ; /** * 删除定时任务 */ ResultData deleteJob(String jobNameKey, String jobGroupNameCode); }
2. JobServiceImpl.java
- 如下:
package com.liu.susu.service.task.impl; import com.liu.susu.common.ResultData; import com.liu.susu.common.ReturnCode; import com.liu.susu.mapper.task.JobMapper; import com.liu.susu.pojo.entity.sys.SysJobManagerEntity; import com.liu.susu.service.task.JobService; import com.liu.susu.task.quartz.vobo.vo.JobAndTriggerVo; import com.liu.susu.utils.JobUtil; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service @Slf4j public class JobServiceImpl implements JobService { @Autowired private Scheduler scheduler; @Autowired private JobMapper jobMapper; /** * 查询定时任务列表 */ @Override public List<JobAndTriggerVo> selectAllJobList() { return jobMapper.list(); } /** * 添加并启动定时任务 */ @Override public void addJob(SysJobManagerEntity form) throws Exception { // 启动调度器 scheduler.start(); // 构建Job信息 JobDetail jobDetail = JobBuilder.newJob(JobUtil.getClass(form.getJobClassName()).getClass()) .withIdentity(form.getJobNameKey(), form.getJobGroupNameCode()).build(); // Cron表达式调度构建器(即任务执行的时间) CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(form.getJobCron()); //根据Cron表达式构建一个Trigger CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(form.getJobNameKey(), form.getJobGroupNameCode()) .withSchedule(cron).build(); try { scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { log.error("【定时任务】创建失败!", e); throw new Exception("【定时任务】创建失败!"); } } /** * 暂停定时任务 */ @Override public ResultData pauseJob(String jobNameKey, String jobGroupNameCode) { try { scheduler.pauseJob(JobKey.jobKey(jobNameKey, jobGroupNameCode)); return new ResultData(ReturnCode.SUCCESS_000000,"暂停成功!"); } catch (SchedulerException e) { e.printStackTrace(); return new ResultData(ReturnCode.FAIL_999999); } } /** * 恢复定时任务 */ @Override public ResultData resumeJob(String jobNameKey, String jobGroupNameCode) { try { scheduler.resumeJob(JobKey.jobKey(jobNameKey, jobGroupNameCode)); return new ResultData(ReturnCode.SUCCESS_000000,"定时任务恢复成功!"); } catch (SchedulerException e) { e.printStackTrace(); return new ResultData(ReturnCode.FAIL_999999); } } /** * 修改定时任务 */ @Override public ResultData updateJobCron(String jobNameKey, String jobGroupNameCode,String newCron) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobNameKey, jobGroupNameCode); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(newCron); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // 根据Cron表达式构建一个Trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); // 按新的trigger重新设置job执行 scheduler.rescheduleJob(triggerKey, trigger); return new ResultData(ReturnCode.UPDATE_SUCCESS,"定时任务修改成功!"); } catch (SchedulerException e) { log.error("【定时任务】更新失败!", e); e.printStackTrace(); return new ResultData(ReturnCode.FAIL_999999); } } /** * 删除定时任务 */ @Override public ResultData deleteJob(String jobNameKey, String jobGroupNameCode) { try { scheduler.pauseTrigger(TriggerKey.triggerKey(jobNameKey, jobGroupNameCode)); scheduler.unscheduleJob(TriggerKey.triggerKey(jobNameKey, jobGroupNameCode)); scheduler.deleteJob(JobKey.jobKey(jobNameKey, jobGroupNameCode)); return new ResultData(ReturnCode.DELETE_SUCCESS,"定时任务删除成功!"); } catch (SchedulerException e) { e.printStackTrace(); return new ResultData(ReturnCode.FAIL_999999); } } }
3.JobController2.java
- 如下:
package com.liu.susu.controller.task;
import com.github.pagehelper.PageHelper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liu.susu.common.PageParam;
import com.liu.susu.common.ResultData;
import com.liu.susu.common.ReturnCode;
import com.liu.susu.mapper.task.SysJobManagerMapper;
import com.liu.susu.pojo.entity.sys.SysJobManagerEntity;
import com.liu.susu.service.task.JobService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Controller
@RequestMapping("/sys/quartz/task")
@Api(tags = "系统管理-->定时任务管理")
@ApiSupport(author = "乘风破浪",order = 3)
@Slf4j
public class JobController2 {
@Autowired
private JobService jobService;
@Autowired
private SysJobManagerMapper sysJobManagerMapper;
@ApiOperationSupport(author = "乘风破浪",order = 1)
@ApiOperation(value = "查询定时任务列表")
@PostMapping("/selectAllJob")
@ResponseBody
public ResultData selectAllJob(@Valid PageParam pageParam) {
if (pageParam!=null){
PageHelper.startPage(pageParam.getPageNum(),pageParam.getPageSize());
}
return ResultData.getPageBaseResultData(pageParam,jobService.selectAllJobList());
}
@ApiOperationSupport(author = "乘风破浪",order = 2)
@ApiOperation(value = "新增定时任务")
@PostMapping("/addNewJob")
@ResponseBody
public ResultData addNewJob(@RequestBody SysJobManagerEntity form) {
if (form!=null && StringUtils.isNotEmpty(form.getJobGroupNameCode())
&& StringUtils.isNotEmpty(form.getJobNameKey())){
//添加任务之前,先校验是否在可添加的列表中有维护
int count = sysJobManagerMapper.findSysJobManager(form.getJobGroupNameCode(), form.getJobNameKey());
if (count>0){
try {
jobService.addJob(form);
return new ResultData(ReturnCode.INSERT_SUCCESS);
} catch (Exception e) {
return new ResultData(ReturnCode.FAIL_999999);
}
}else {
return new ResultData(ReturnCode.FAIL_999998,"没有维护要新增的定时任务,请先找管理员维护!");
}
}
return new ResultData(ReturnCode.FAIL_NO_PARAM, "表单参数为空,请检查参数");
}
@ApiOperationSupport(author = "乘风破浪",order = 3)
@ApiOperation(value = "暂停任务")
@GetMapping("/pauseJob")
@ResponseBody
public ResultData pauseJob(@RequestParam(value = "jobNameKey",required = true) String jobNameKey,
@RequestParam(value = "jobGroupNameCode",required = true) String jobGroupNameCode){
if (StringUtils.isEmpty(jobNameKey) || StringUtils.isEmpty(jobGroupNameCode)){
return new ResultData(ReturnCode.FAIL_NO_PARAM, "参数不能为空!");
}
return jobService.pauseJob(jobNameKey,jobGroupNameCode);
}
@ApiOperationSupport(author = "乘风破浪",order = 4)
@ApiOperation(value = "恢复定时任务")
@GetMapping("/resumeJob")
@ResponseBody
public ResultData resumeJob(@RequestParam(value = "jobNameKey") String jobNameKey,
@RequestParam(value = "jobGroupNameCode") String jobGroupNameCode) {
if (StringUtils.isEmpty(jobNameKey) || StringUtils.isEmpty(jobGroupNameCode)){
return new ResultData(ReturnCode.FAIL_NO_PARAM, "参数不能为空!");
}
return jobService.resumeJob(jobNameKey,jobGroupNameCode);
}
@ApiOperationSupport(author = "乘风破浪",order = 5)
@ApiOperation(value = "修改定时任务,定时时间")
@GetMapping("/updateJobCron")
@ResponseBody
public ResultData updateJobCron(@Param("jobNameKey") String jobNameKey,
@Param("jobGroupNameCode") String jobGroupNameCode,
@Param("cron") String newCron) {
return jobService.updateJobCron(jobNameKey, jobGroupNameCode, newCron);
}
@ApiOperationSupport(author = "乘风破浪",order = 6)
@ApiOperation(value = "删除定时任务")
@GetMapping("/deleteJob")
@ResponseBody
public ResultData deleteJob(@Param("jobNameKey") String jobNameKey,
@Param("jobGroupNameCode") String jobGroupNameCode) {
jobService.deleteJob(jobNameKey,jobGroupNameCode);
return new ResultData(ReturnCode.DELETE_SUCCESS, "删除成功!");
}
}
8.3.2 下载地址
我把这个集成到自己项目里了,优化后的代码也在,代码太多,所以就贴上面部分代码了,其他的自己根据需要可自行下载:
边栏推荐
- Flink实时仓库-DWD层(kafka-关联mysql的lookup join)模板代码
- H3C_利用设置缺省静态路由优先级实现出口双线路的主备功能
- 后缀自动机(SAM)讲解 + Luogu p3804【模板】后缀自动机 (SAM)
- [cf1054h] epic Revolution -- number theory, convolution, arbitrary modulus NTT
- Junda technology | applicable to "riyueyuan" brand ups wechat cloud monitoring card
- Improved pillar with fine grained feature for 3D object detection paper notes
- Summary of OCR optical character recognition methods
- Is online legend software testing training really so black hearted? Are they all scams?
- vagrant box 集群 处理
- spark学习笔记(七)——sparkcore核心编程-RDD序列化/依赖关系/持久化/分区器/累加器/广播变量
猜你喜欢

Win11vmware turns on the virtual machine and restarts on the blue screen and the solution that cannot be started

vim文本编辑器的一些使用小技巧

上采样之反卷积操作

外包干了3年,跳槽后转自动化测试工资是原来的2倍,秘诀原来是......

Why does ETL often become ELT or even let?

Thread - thread safety - thread optimization

Comparison of advantages between can & canfd integrated test analysis software lkmaster and PCA Explorer 6 analysis software

2D cartoon rendering - advanced skills

约瑟夫环问题

Nodejs安装教程
随机推荐
Junda technology | applicable to "riyueyuan" brand ups wechat cloud monitoring card
[solution] error: lib/bridge_ generated. dart:837:9: Error: The parameter ‘ptr‘ of the method ‘FlutterRustB
Flink real-time warehouse DWD layer (processing complex data - installation and replacement of streams and tables) template code
pytorch的技巧记录
OCR光学字符识别方法汇总
CVPR2021| 基于自监督学习的多视图立体匹配 (CVPR2021)
2022-07-28:以下go语言代码输出什么?A:AA;B:AB;C:BA;D:BB。 package main import ( “fmt“ ) func main() { f
我的个人网站不让接入微信登录,于是我做了这个
gin 路由,参数,输出
Leetcode 879. profit plan
Spark Learning Notes (VII) -- spark core core programming - RDD serialization / dependency / persistence / partition / accumulator / broadcast variables
Vite3.0都发布了,你还能卷得动吗(新特性一览)
MySQL----多表查询
Implementation of book borrowing management system based on C language
Cesium reflection
Cesium反射
IO stream - file - properties
【C语言刷LeetCode】67. 二进制求和(E)
[C language brush leetcode] 1054. Bar code with equal distance (m)
Overview of database system