前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转 Spring Boot 集成篇(任务动态管理代码篇)

玩转 Spring Boot 集成篇(任务动态管理代码篇)

作者头像
botkenni
发布2022-12-27 17:12:46
5210
发布2022-12-27 17:12:46
举报
文章被收录于专栏:IT码农IT码农

在日常项目研发中,定时任务可谓是必不可少的一环,如果面对任务执行周期固定,业务简单的场景,可直接使用 Spring Boot 内置注解方式实现任务;而如果考虑更为复杂的管理任务信息,在可以通过集成 Quartz 等开源轮子来助力业务研发。

本次主要分享一下 Spring Boot 集成 Quartz 任务框架后,如何实现任务的动态管理,更能够让研发人员专注业务任务的研发,那么就要逐一解决如下疑问。

疑问:是否可以通过 API 动态创建任务呢?

疑问:是否可以通过 API 编辑任务的执行时间呢?

疑问:是否可以通过 API 暂停/恢复任务呢?

疑问:是否可以通过 API 删除任务呢?

疑问:是否可以通过页面完成任务的 CRUD 呢?

考虑到下面的操作是一个大工程,为了方便,重新开启一个 Spring Boot 项目,为了进一步熟练使用 Spring Boot 相关各种 starter,本次选用 MyBatis 作为持久层框架。

1. 核心管理代码

1.1 任务控制器

定义 TaskController,提供用户操作任务的相关 API,例如查询任务列表、添加任务、暂停任务、恢复任务、删除任务。

package com.example.demo.quartz.controller; import com.example.demo.quartz.common.Result; import com.example.demo.quartz.service.TaskInfoService; import com.example.demo.quartz.vo.TaskInfoReq; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /**  * 定时任务管理  **/ @RestController @RequestMapping("/task") public class TaskController {     @Autowired     private TaskInfoService taskInfoService;     /**定时器列表*/     @PostMapping("/list")     public Result list(@RequestBody TaskInfoReq reqVo) {         return taskInfoService.selectTaskListByPage(reqVo);     }     /**定时器修改*/     @PostMapping("/edit")     public Result edit(@RequestBody TaskInfoReq reqVo) {         return taskInfoService.updateJob(reqVo);     }     /**暂停任务*/     @PostMapping("/pause")     public Result pause(Integer taskId) {         return taskInfoService.pauseJob(taskId);     }     /**增加任务*/     @PostMapping("/add")     public Result add(@RequestBody TaskInfoReq taskInfoReq) {         return taskInfoService.addJob(taskInfoReq);     }     /**恢复任务*/     @PostMapping("/resume")     public Result resume(Integer taskId) {         return taskInfoService.resumeJob(taskId);     }     /**删除任务*/     @PostMapping("/del")     public Result delete(@RequestBody TaskInfoReq reqVo) {         return taskInfoService.delete(reqVo);     } }

1.2 任务管理

TaskManager 任务管理器,主要接收业务指令,来完成对 Quartz 容器进行操作。

package com.example.demo.quartz.task; import com.example.demo.quartz.entity.TaskInfo; import com.example.demo.quartz.utils.SpringContextUtils; import com.example.demo.quartz.vo.TaskInfoReq; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /**  * 任务管理  * 1、添加任务 2、更新任务 3、暂停任务 4、恢复任务  **/ @Component public class TaskManager {     private static final Logger LOGGER = LoggerFactory.getLogger(TaskManager.class);     public static final String JOB_DEFAULT_GROUP_NAME = "JOB_DEFAULT_GROUP_NAME";     public static final String TRIGGER_DEFAULT_GROUP_NAME = "TRIGGER_DEFAULT_GROUP_NAME";     @Autowired     private Scheduler scheduler;     @Autowired     private SpringContextUtils springContextUtils;     /**      * 添加任务      */     public boolean addJob(TaskInfoReq taskInfoReq) {         boolean flag = true;         if (!CronExpression.isValidExpression(taskInfoReq.getCron())) {             LOGGER.error("定时任务表达式有误:{}", taskInfoReq.getCron());             return false;         }         try {             String className = springContextUtils.getBean(taskInfoReq.getJobName()).getClass().getName();             JobDetail jobDetail = JobBuilder.newJob().withIdentity(new JobKey(taskInfoReq.getJobName(), JOB_DEFAULT_GROUP_NAME))                     .ofType((Class<Job>) Class.forName(className))                     .build();             Trigger trigger = TriggerBuilder.newTrigger()                     .forJob(jobDetail)                     .withSchedule(CronScheduleBuilder.cronSchedule(taskInfoReq.getCron()))                     .withIdentity(new TriggerKey(taskInfoReq.getJobName(), TRIGGER_DEFAULT_GROUP_NAME))                     .build();             scheduler.scheduleJob(jobDetail, trigger);             scheduler.start();         } catch (Exception e) {             LOGGER.error("添加定时任务异常:{}", e.getMessage(), e);             flag = false;         }         return flag;     }     /**      * 更新任务      */     public boolean updateJob(TaskInfo taskInfo) {         boolean flag = true;         try {             JobKey jobKey = new JobKey(taskInfo.getJobName(), JOB_DEFAULT_GROUP_NAME);             TriggerKey triggerKey = new TriggerKey(taskInfo.getJobName(), TRIGGER_DEFAULT_GROUP_NAME);             JobDetail jobDetail = scheduler.getJobDetail(jobKey);             if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) {                 Trigger newTrigger = TriggerBuilder.newTrigger()                         .forJob(jobDetail)                         .withSchedule(CronScheduleBuilder.cronSchedule(taskInfo.getCron()))                         .withIdentity(triggerKey)                         .build();                 scheduler.rescheduleJob(triggerKey, newTrigger);             } else {                 LOGGER.info("更新任务失败,任务不存在,任务名称:{},表达式:{}", taskInfo.getJobName(), taskInfo.getCron());             }             LOGGER.info("更新任务成功,任务名称:{},表达式:{}", taskInfo.getJobName(), taskInfo.getCron());         } catch (SchedulerException e) {             LOGGER.error("更新定时任务失败:{}", e.getMessage(), e);             flag = false;         }         return flag;     }     /**      * 暂停任务      */     public boolean pauseJob(TaskInfo taskInfo) {         try {             scheduler.pauseJob(JobKey.jobKey(taskInfo.getJobName(), JOB_DEFAULT_GROUP_NAME));             LOGGER.info("任务暂停成功:{}", taskInfo.getId());             return true;         } catch (SchedulerException e) {             LOGGER.error("暂停定时任务失败:{}", e.getMessage(), e);             return false;         }     }     /**      * 恢复任务      */     public boolean resumeJob(TaskInfo taskInfo) {         try {             scheduler.resumeJob(JobKey.jobKey(taskInfo.getJobName(), JOB_DEFAULT_GROUP_NAME));             LOGGER.info("任务恢复成功:{}", taskInfo.getId());             return true;         } catch (SchedulerException e) {             LOGGER.error("恢复定时任务失败:{}", e.getMessage(), e);             return false;         }     } }

1.3 启动管理

1.3.1 QuartzManager

Spring Boot 容器启动时,加载启动所有任务。

package com.example.demo.quartz.config; import com.example.demo.quartz.common.EnumTaskEnable; import com.example.demo.quartz.entity.TaskInfo; import com.example.demo.quartz.service.TaskInfoService; import com.example.demo.quartz.vo.TaskInfoReq; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; import java.util.List; @Component public class QuartzManager {     private Logger logger = LoggerFactory.getLogger(QuartzManager.class);     @Autowired     private Scheduler scheduler;     @Autowired     private SpringJobFactory springJobFactory;     @Autowired     private TaskInfoService taskInfoService;     @PostConstruct     public void start() {         //启动所有任务         try {             scheduler.setJobFactory(springJobFactory);             // scheduler.clear();             List<TaskInfo> tasks = taskInfoService.selectTasks();             for (TaskInfo taskInfo : tasks) {                 if (EnumTaskEnable.START.getCode().equals(taskInfo.getStatus()) && !StringUtils.isEmpty(taskInfo.getCron())) {                     TaskInfoReq data=new TaskInfoReq();                     BeanUtils.copyProperties(taskInfo,data);                     taskInfoService.addJob(data);                 }             }             logger.info("定时任务启动完成");         } catch (SchedulerException e) {             logger.error(e.getMessage(), e);             throw new RuntimeException("定时任务初始化失败");         }     } }

1.3.2 SpringJobFactory

package com.example.demo.quartz.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /**  * 解决spring bean注入Job的问题  */ @Component public class SpringJobFactory extends AdaptableJobFactory {     @Autowired     private AutowireCapableBeanFactory capableBeanFactory;     @Override     protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {         // 调用父类的方法         Object jobInstance = super.createJobInstance(bundle);         // 进行注入         capableBeanFactory.autowireBean(jobInstance);         return jobInstance;     } }

2. 支撑代码(表、entity、dao、service、utils)

支撑代码主要完成数据库的 CRUD 操作,实现方式很多种,不局限于 MyBatis,主要是抽象思想:能往数据库插入任务记录、查询任务记录就行。

2.1 任务信息表

CREATE TABLE `SC_TASK_INFO` (   `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',   `cron` varchar(32) DEFAULT NULL COMMENT '定时执行',   `job_name` varchar(256) DEFAULT NULL COMMENT '任务名称',   `status` char(1) DEFAULT '0' COMMENT '任务开启状态 0-关闭 2-开启',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   `update_time` datetime DEFAULT NULL COMMENT '更新时间',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT COMMENT='定时任务表';

2.2 实体类

package com.example.demo.quartz.entity; import lombok.Data; import java.io.Serializable; import java.util.Date; @Data public class TaskInfo implements Serializable {     private Integer id;     private String cron;     private String jobName;     private String status;     private Date createTime;     private Date updateTime; }

2.3 TaskInfoDao 定义

package com.example.demo.quartz.dao; import com.example.demo.quartz.entity.TaskInfo; import com.example.demo.quartz.vo.TaskInfoReq; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface TaskInfoDao {     TaskInfo selectByJobName(String jobName);     List<TaskInfo> selectAll();     List<TaskInfo> selectTaskInfos(TaskInfoReq taskInfo);     int deleteByPrimaryKey(Integer id);     int insertSelective(TaskInfo record);     TaskInfo selectByPrimaryKey(Integer id);     int updateByPrimaryKeySelective(TaskInfo record); }

2.4 TaskInfoService 定义

package com.example.demo.quartz.service; import com.example.demo.quartz.common.Result; import com.example.demo.quartz.entity.TaskInfo; import com.example.demo.quartz.vo.TaskInfoReq; import java.util.List; /**  * 定时任务接口  **/ public interface TaskInfoService {     /**获取任务列表分页*/     Result selectTaskListByPage(TaskInfoReq taskInfoReq);     /**添加定时任务*/     Result addJob(TaskInfoReq taskInfoReq);     /**更新任务*/     Result updateJob(TaskInfoReq taskInfoReq);     /**暂停任务*/     Result pauseJob(Integer taskId);     /**恢复任务*/     Result resumeJob(Integer taskId);     /**获取所有任务*/     List<TaskInfo> selectTasks();     /**删除任务*/     Result delete(TaskInfoReq reqVo); }

2.5 TaskInfoServiceImpl 业务实现类定义

package com.example.demo.quartz.service.impl; import com.example.demo.quartz.common.CodeMsg; import com.example.demo.quartz.common.EnumTaskEnable; import com.example.demo.quartz.common.ResponseFactory; import com.example.demo.quartz.common.Result; import com.example.demo.quartz.dao.TaskInfoDao; import com.example.demo.quartz.entity.TaskInfo; import com.example.demo.quartz.service.TaskInfoService; import com.example.demo.quartz.task.TaskManager; import com.example.demo.quartz.vo.TaskInfoReq; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.quartz.CronExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Date; import java.util.List; import java.util.Objects; /**  * 定时任务业务实现  **/ @Service public class TaskInfoServiceImpl implements TaskInfoService {     private static final Logger LOGGER = LoggerFactory.getLogger(TaskInfoServiceImpl.class);     @Resource     private TaskInfoDao taskInfoDao;     @Resource     private TaskManager taskManager;     @Override     public Result selectTaskListByPage(TaskInfoReq taskInfoReq) {         PageHelper.startPage(taskInfoReq.getPageCurrent(), taskInfoReq.getPageSize());         List<TaskInfo> list = taskInfoDao.selectTaskInfos(taskInfoReq);         PageInfo<TaskInfo> pageInfo = new PageInfo<>(list);         return ResponseFactory.build(pageInfo);     }     @Override     @Transactional(rollbackFor = Exception.class)     public Result updateJob(TaskInfoReq taskInfoReq) {         if (!CronExpression.isValidExpression(taskInfoReq.getCron())) {             LOGGER.error("更新任务失败,表达式有误:{}", taskInfoReq.getCron());             return ResponseFactory.build(CodeMsg.TASK_CRON_ERROR);         }         TaskInfo isExistData = taskInfoDao.selectByJobName(taskInfoReq.getJobName());         //当任务存在,则更改失败         if ((!Objects.isNull(isExistData)) && (!isExistData.getId().equals(taskInfoReq.getId()))) {             return ResponseFactory.build(CodeMsg.TASK_CRON_DOUBLE);         }         TaskInfo data = taskInfoDao.selectByPrimaryKey(taskInfoReq.getId());         if (data == null) {             return ResponseFactory.build(CodeMsg.TASK_NOT_EXITES);         }         BeanUtils.copyProperties(taskInfoReq, data);         data.setUpdateTime(new Date());         taskInfoDao.updateByPrimaryKeySelective(data);         if (!taskManager.updateJob(data)) {             return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);         }         return ResponseFactory.build();     }     @Override     public Result pauseJob(Integer taskId) {         TaskInfo data = taskInfoDao.selectByPrimaryKey(taskId);         if (data == null) {             return ResponseFactory.build(CodeMsg.TASK_NOT_EXITES);         }         if (!taskManager.pauseJob(data)) {             return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);         }         data.setStatus(EnumTaskEnable.STOP.getCode());         taskInfoDao.updateByPrimaryKeySelective(data);         return ResponseFactory.build();     }     @Override     public Result resumeJob(Integer taskId) {         TaskInfo data = taskInfoDao.selectByPrimaryKey(taskId);         if (data == null) {             return ResponseFactory.build(CodeMsg.TASK_NOT_EXITES);         }         if (!taskManager.resumeJob(data)) {             return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);         }         data.setStatus(EnumTaskEnable.START.getCode());         taskInfoDao.updateByPrimaryKeySelective(data);         return ResponseFactory.build();     }     @Override     public Result addJob(TaskInfoReq taskInfoReq) {         if (!taskManager.addJob(taskInfoReq)) {             return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);         }         TaskInfo data = taskInfoDao.selectByJobName(taskInfoReq.getJobName());         //当任务不存在,则返回成功插入         if (Objects.isNull(data)) {             data = new TaskInfo();             BeanUtils.copyProperties(taskInfoReq, data);             data.setCreateTime(new Date());             taskInfoDao.insertSelective(data);             return ResponseFactory.build();         } else {             return ResponseFactory.build(CodeMsg.TASK_CRON_DOUBLE);         }     }     @Override     public Result delete(TaskInfoReq reqVo) {         try {             //TODO 删除任务只是做了暂停,如果是 Quartz Jdbc 模式下添加重复任务可能加不进去,并没有真正删除(可自行调整)             Result result = this.pauseJob(reqVo.getId());             //只有暂停成功的任务才能删除             if (CodeMsg.SUCCESS == result.getCode()) {                 taskInfoDao.deleteByPrimaryKey(reqVo.getId());                 return ResponseFactory.build();             } else {                 return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);             }         } catch (Exception e) {             return ResponseFactory.build(CodeMsg.TASK_EXCEPTION);         }     }     @Override     public List<TaskInfo> selectTasks() {         return taskInfoDao.selectAll();     } }

2.6 TaskInfoMapper.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.example.demo.quartz.dao.TaskInfoDao">     <resultMap id="BaseResultMap" type="com.example.demo.quartz.entity.TaskInfo">         <id column="id" property="id" jdbcType="INTEGER"/>         <result column="cron" property="cron" jdbcType="VARCHAR"/>         <result column="job_name" property="jobName" jdbcType="VARCHAR"/>         <result column="status" property="status" jdbcType="CHAR"/>         <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>         <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>     </resultMap>     <sql id="Base_Column_List">     id, cron, job_name, status, create_time, update_time   </sql>     <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">         select         <include refid="Base_Column_List"/>         from sc_task_info         where id = #{id,jdbcType=INTEGER}     </select>     <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">     delete from sc_task_info     where id = #{id,jdbcType=INTEGER}   </delete>     <insert id="insert" parameterType="com.example.demo.quartz.entity.TaskInfo">         <selectKey resultType="java.lang.Integer" keyProperty="id" order="AFTER">             SELECT LAST_INSERT_ID()         </selectKey>         insert into sc_task_info (cron, job_name, status,         create_time, update_time)         values (#{cron,jdbcType=VARCHAR}, #{jobName,jdbcType=VARCHAR}, #{status,jdbcType=CHAR},         #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP})     </insert>     <insert id="insertSelective" parameterType="com.example.demo.quartz.entity.TaskInfo">         <selectKey resultType="java.lang.Integer" keyProperty="id" order="AFTER">             SELECT LAST_INSERT_ID()         </selectKey>         insert into sc_task_info         <trim prefix="(" suffix=")" suffixOverrides=",">             <if test="cron != null">                 cron,             </if>             <if test="jobName != null">                 job_name,             </if>             <if test="status != null">                 status,             </if>             <if test="createTime != null">                 create_time,             </if>             <if test="updateTime != null">                 update_time,             </if>         </trim>         <trim prefix="values (" suffix=")" suffixOverrides=",">             <if test="cron != null">                 #{cron,jdbcType=VARCHAR},             </if>             <if test="jobName != null">                 #{jobName,jdbcType=VARCHAR},             </if>             <if test="status != null">                 #{status,jdbcType=CHAR},             </if>             <if test="createTime != null">                 #{createTime,jdbcType=TIMESTAMP},             </if>             <if test="updateTime != null">                 #{updateTime,jdbcType=TIMESTAMP},             </if>         </trim>     </insert>     <update id="updateByPrimaryKeySelective" parameterType="com.example.demo.quartz.entity.TaskInfo">         update sc_task_info         <set>             <if test="cron != null">                 cron = #{cron,jdbcType=VARCHAR},             </if>             <if test="jobName != null">                 job_name = #{jobName,jdbcType=VARCHAR},             </if>             <if test="status != null">                 status = #{status,jdbcType=CHAR},             </if>             <if test="createTime != null">                 create_time = #{createTime,jdbcType=TIMESTAMP},             </if>             <if test="updateTime != null">                 update_time = #{updateTime,jdbcType=TIMESTAMP},             </if>         </set>         where id = #{id,jdbcType=INTEGER}     </update>     <update id="updateByPrimaryKey" parameterType="com.example.demo.quartz.entity.TaskInfo">     update sc_task_info     set cron = #{cron,jdbcType=VARCHAR},       job_name = #{jobName,jdbcType=VARCHAR},       status = #{status,jdbcType=CHAR},       create_time = #{createTime,jdbcType=TIMESTAMP},       update_time = #{updateTime,jdbcType=TIMESTAMP}     where id = #{id,jdbcType=INTEGER}   </update>     <select id="selectByJobName" resultMap="BaseResultMap"             parameterType="java.lang.String">           select * from sc_task_info where job_name=#{jobName}      </select>     <select id="selectAll" resultMap="BaseResultMap">           select * from sc_task_info      </select>     <select id="selectTaskInfos" resultMap="BaseResultMap"             parameterType="com.example.demo.quartz.vo.TaskInfoReq">         select * from sc_task_info         <where>             <if test="searchKey != null and searchKey != ''">job_name like concat('%',concat(trim(#{searchKey}),'%'))             </if>             <if test="status != null and status != ''">and status=#{status}</if>         </where>     </select> </mapper>

3. 工具类&辅助代码

3.1 TaskInfoReq 类

package com.example.demo.quartz.vo; import lombok.Data; /**  * 任务请求类  **/ @Data public class TaskInfoReq {     /**任务编号*/     private Integer id;     /**任务时间表达式*/     private String cron;     /**任务状态*/     private String status;     /**任务名称*/     private String jobName;     /**每页显示条数*/     private int pageSize=10;     /**当前页数*/     private int pageCurrent=1; }

3.2 Result 类定义

package com.example.demo.quartz.common; import lombok.Data; @Data public class Result {     private int code;     private String msg;     private Object retData; }

3.3 响应工具类封装

package com.example.demo.quartz.common; /**  * 响应工具类  */ public class ResponseFactory {     private static Result commonBuild(int code, String errmsg) {         Result result = new Result();         result.setCode(code);         if (errmsg == null || errmsg.trim().length() == 0) {             result.setMsg(CodeMsg.getMsg(code));         } else {             result.setMsg(errmsg);         }         return result;     }     public static Result build(int code) {         return commonBuild(code, CodeMsg.getMsg(code));     }     public static Result build() {         return commonBuild(CodeMsg.SUCCESS, null);     }     public static Result build(Object data) {         Result json = commonBuild(CodeMsg.SUCCESS, null);         json.setRetData(data);         return json;     } }

3.4 任务状态枚举类

package com.example.demo.quartz.common; public enum EnumTaskEnable {     START("2", "开启"),     STOP("0", "关闭");     private String code;     private String msg;     EnumTaskEnable(String code, String msg) {         this.code = code;         this.msg = msg;     }     public String getCode() {         return code;     } }

3.5 公共返回码

package com.example.demo.quartz.common; import java.util.HashMap; import java.util.Map; /**  * 公共返回码  */ public class CodeMsg {     private static final Map<Integer, String> MSG = new HashMap<Integer, String>();     //系统     public static final int SUCCESS = 200;     public static final int ERROR = 500;     //任务     public static final int TASK_NOT_EXITES = 100001;     public static final int TASK_EXCEPTION = 100002;     public static final int TASK_CRON_ERROR = 100003;     public static final int TASK_CRON_DOUBLE = 100004;     static {         //系统         MSG.put(SUCCESS, "请求成功.");         MSG.put(ERROR, "服务器异常.");         //任务         MSG.put(TASK_NOT_EXITES, "定时任务不存在");         MSG.put(TASK_EXCEPTION, "设置定时任务失败");         MSG.put(TASK_CRON_ERROR, "表达式有误");         MSG.put(TASK_CRON_DOUBLE, "定时任务已经存在");     }     public static String getMsg(int errcode) {         return MSG.get(errcode);     } }

3.6 SpringContextUtils 工具类

package com.example.demo.quartz.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component @Lazy(false) public class SpringContextUtils implements ApplicationContextAware {     // Spring应用上下文环境     private static ApplicationContext applicationContext;     /**      * 实现ApplicationContextAware接口的回调方法,设置上下文环境      *      * @param applicationContext      */     public void setApplicationContext(ApplicationContext applicationContext) {         SpringContextUtils.applicationContext = applicationContext;     }     /**      * @return ApplicationContext      */     public static ApplicationContext getApplicationContext() {         return applicationContext;     }     /**      * 获取对象      * 这里重写了bean方法,起主要作用      *      * @param name      * @return Object 一个以所给名字注册的bean的实例      * @throws BeansException      */     public static <T> T getBean(String name) {         try {             return (T) applicationContext.getBean(name);         } catch (Exception e) {             return null;         }     }     public static <T> T getBean(Class<T> clazz) {         try {             return applicationContext.getBean(clazz);         } catch (Exception e) {             return null;         }     } }

3.7 配置文件

3.7.1 application.properties

server.port=${random.int[10000,19999]} #server.port=15158 ## 将 Quartz 持久化方式修改为 jdbc spring.quartz.job-store-type=jdbc ## 实例名称(默认为quartzScheduler) spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler ## 实例节点 ID 自动生成 spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO ## 修改存储内容使用的类 spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX ## 数据源信息 spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobs spring.quartz.properties.org.quartz.dataSource.quartz_jobs.driver=com.mysql.cj.jdbc.Driver spring.quartz.properties.org.quartz.dataSource.quartz_jobs.URL=jdbc:mysql://127.0.0.1:3306/quartz_jobs?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 spring.quartz.properties.org.quartz.dataSource.quartz_jobs.user=root spring.quartz.properties.org.quartz.dataSource.quartz_jobs.password=123456 ## 开启集群,多个 Quartz 实例使用同一组数据库表 spring.quartz.properties.org.quartz.jobStore.isClustered=true # MySQL 链接信息 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/quartz_jobs?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ## MyBatis 的配置 # Mapper资源文件存放的路径 mybatis.mapper-locations=classpath*:mapper/*.xml # Dao 接口文件存放的目录 mybatis.type-aliases-package=com.example.demo.quartz.dao # 开启 debug,输出 SQL logging.level.com.example.demo.dao=debug #pagehelper propertis文件分页插件配置 pagehelper.helperDialect=mysql pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.params.count=countSql

备注:考虑到部署成本问题,若是单机、内存方式存储任务信息,则只可把 Quartz 相关配置通通去掉。

3.7.2 pom.xml 

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>2.6.3</version>         <relativePath/> <!-- lookup parent from repository -->     </parent>     <groupId>com.example</groupId>     <artifactId>demo_job</artifactId>     <version>0.0.1-SNAPSHOT</version>     <name>demo_job</name>     <description>Demo project for Spring Boot</description>     <properties>         <java.version>1.8</java.version>     </properties>     <dependencies>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-quartz</artifactId>         </dependency>         <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>         </dependency>         <dependency>             <groupId>com.mchange</groupId>             <artifactId>c3p0</artifactId>             <version>0.9.5.4</version>         </dependency>         <dependency>             <groupId>com.github.pagehelper</groupId>             <artifactId>pagehelper-spring-boot-starter</artifactId>             <version>1.4.1</version>         </dependency>         <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>             <version>1.18.22</version>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>     </dependencies>     <build>         <plugins>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>             </plugin>         </plugins>     </build> </project>

4. 任务代码

定义要执行的业务逻辑任务 DongAoJob 类,这块也就是研发人员重点关注的,后续只需实现 Job 业务就行。

package com.example.demo.quartz.task; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Component; /**  * 定义一个调度器要执行的任务  */ @Component public class DongAoJob extends QuartzJobBean {     private static final Log logger = LogFactory.getLog(DongAoJob.class);     @Override     protected void executeInternal(JobExecutionContext context) throws JobExecutionException {         logger.info("幼年是盼盼,青年是晶晶,中年是冰墩墩,生活见好逐渐发福");     } }

5. 程序入口

package com.example.demo.quartz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoJobApplication {     public static void main(String[] args) {         SpringApplication.run(DemoJobApplication.class, args);     } }

6. 运行验证

其实挂个简单页面就能轻松完成页面化配置任务,本次用 Postman 方式直接调用任务管理的 API。

6.1 添加任务

289e534c07f3cc87482eea453279404b.png
289e534c07f3cc87482eea453279404b.png

此时库任务 Id 为7:

ad30fc0200b51b47f518c3fd54ba937c.png
ad30fc0200b51b47f518c3fd54ba937c.png

控制台:

e0d3c01ecf5a24d5dc48cd589729b9c9.png
e0d3c01ecf5a24d5dc48cd589729b9c9.png

6.2 查询任务

435f8570b9692f35732f315d0cc2f9f7.png
435f8570b9692f35732f315d0cc2f9f7.png

6.3 编辑任务

d9bccb5b676d3c780d15f443d45c3a34.png
d9bccb5b676d3c780d15f443d45c3a34.png

控制台输出:

1a8d501bfbbd92cd993f4efe3d5335ea.png
1a8d501bfbbd92cd993f4efe3d5335ea.png

6.4 暂停任务

http://localhost:15158/task/pause?taskId=7

750fdfc33a7ebbd36bb5fac082f275d0.png
750fdfc33a7ebbd36bb5fac082f275d0.png

6.5 恢复任务

http://localhost:15158/task/resume?taskId=7

d6b3b63ceeee1155e59da44ad31f6d29.png
d6b3b63ceeee1155e59da44ad31f6d29.png

7. 例行回顾

本文是 Spring Boot 项目集成 Quartz 来实现任务的动态管理,主要是代码,感兴趣的可以自行拿去验证改造并用于实践。

74bde8495fdf716ed0f2bf80aa28c322.png
74bde8495fdf716ed0f2bf80aa28c322.png
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库一体机 TData
数据库一体机 TData 是融合了高性能计算、热插拔闪存、Infiniband 网络、RDMA 远程直接存取数据的数据库解决方案,为用户提供高可用、易扩展、高性能的数据库服务,适用于 OLAP、 OLTP 以及混合负载等各种应用场景下的极限性能需求,支持 Oracle、SQL Server、MySQL 和 PostgreSQL 等各种主流数据库。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档