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

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

作者头像
一猿小讲
发布2022-02-25 09:51:32
1.6K6
发布2022-02-25 09:51:32
举报
文章被收录于专栏:一猿小讲一猿小讲

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

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

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

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

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

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

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

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

1. 核心管理代码

1.1 任务控制器

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

代码语言:javascript
复制
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 容器进行操作。

代码语言:javascript
复制
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 容器启动时,加载启动所有任务。

代码语言:javascript
复制
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

代码语言:javascript
复制
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 任务信息表

代码语言:javascript
复制
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 实体类

代码语言:javascript
复制
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 定义

代码语言:javascript
复制
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 定义

代码语言:javascript
复制
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 业务实现类定义

代码语言:javascript
复制
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 文件

代码语言:javascript
复制
<?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 类

代码语言:javascript
复制
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 类定义

代码语言:javascript
复制
package com.example.demo.quartz.common;
import lombok.Data;

@Data
public class Result {
    private int code;
    private String msg;
    private Object retData;
}

3.3 响应工具类封装

代码语言:javascript
复制
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 任务状态枚举类

代码语言:javascript
复制
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 公共返回码

代码语言:javascript
复制
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 工具类

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
<?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 业务就行。

代码语言:javascript
复制
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. 程序入口

代码语言:javascript
复制
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 添加任务

此时库任务 Id 为7:

控制台:

6.2 查询任务

6.3 编辑任务

控制台输出:

6.4 暂停任务

代码语言:javascript
复制
http://localhost:15158/task/pause?taskId=7

6.5 恢复任务

代码语言:javascript
复制
http://localhost:15158/task/resume?taskId=7

7. 例行回顾

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

玩转 Spring Boot 集成定时任务篇就写到这里,希望大家能够喜欢。

历史系列文章:

玩转 Spring Boot 入门篇 玩转 Spring Boot 集成篇(MySQL、Druid、HikariCP) 玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持) 玩转 Spring Boot 集成篇(Redis) 玩转 Spring Boot 集成篇(Actuator、Spring Boot Admin) 玩转 Spring Boot 集成篇(RabbitMQ) 玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-02-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一猿小讲 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档