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

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

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

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

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

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

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

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

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

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第1张图片

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) 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 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 selectAll();
    List 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 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 list = taskInfoDao.selectTaskInfos(taskInfoReq);
        PageInfo 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 selectTasks() {
        return taskInfoDao.selectAll();
    }
}

2.6 TaskInfoMapper.xml 文件






    
        
        
        
        
        
        
    
    
    id, cron, job_name, status, create_time, update_time
  
    
    
    delete from sc_task_info
    where id = #{id,jdbcType=INTEGER}
  
    
        
            SELECT LAST_INSERT_ID()
        
        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})
    
    
        
            SELECT LAST_INSERT_ID()
        
        insert into sc_task_info
        
            
                cron,
            
            
                job_name,
            
            
                status,
            
            
                create_time,
            
            
                update_time,
            
        
        
            
                #{cron,jdbcType=VARCHAR},
            
            
                #{jobName,jdbcType=VARCHAR},
            
            
                #{status,jdbcType=CHAR},
            
            
                #{createTime,jdbcType=TIMESTAMP},
            
            
                #{updateTime,jdbcType=TIMESTAMP},
            
        
    
    
        update sc_task_info
        
            
                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 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}
  


    


    


    

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 MSG = new HashMap();


    //系统
    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 getBean(String name) {
        try {
            return (T) applicationContext.getBean(name);
        } catch (Exception e) {
            return null;
        }
    }


    public static  T getBean(Class 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 



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.3
         
    
    com.example
    demo_job
    0.0.1-SNAPSHOT
    demo_job
    Demo project for Spring Boot
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-quartz
        
        
            mysql
            mysql-connector-java
        
        
            com.mchange
            c3p0
            0.9.5.4
        
        
            com.github.pagehelper
            pagehelper-spring-boot-starter
            1.4.1
        
        
            org.projectlombok
            lombok
            1.18.22
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

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 添加任务

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第2张图片

此时库任务 Id 为7:

ad30fc0200b51b47f518c3fd54ba937c.png

控制台:

e0d3c01ecf5a24d5dc48cd589729b9c9.png

6.2 查询任务

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第3张图片

6.3 编辑任务

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第4张图片

控制台输出:

1a8d501bfbbd92cd993f4efe3d5335ea.png

6.4 暂停任务

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

750fdfc33a7ebbd36bb5fac082f275d0.png

6.5 恢复任务

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

d6b3b63ceeee1155e59da44ad31f6d29.png

7. 例行回顾

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

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第5张图片

玩转 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、静态、动态定时任务)

玩转 Spring Boot 集成篇(任务动态管理代码篇)_第6张图片

你可能感兴趣的:(java,spring,mysql,mybatis,spring,boot)