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

Spring中使用定时任务的两种方式[@Scheduled,quartz+SpringBeanJobFactory] 文章目录Spring自带的@Scheduled注解1. 在主启动类上面加@EnableScheduling,开启定时任务2. 定时任务类3. 开启异步执行定时任务,在主启动类上面加@EnableScheduling4. 设置异步执行定时任务的线程池大小Spring中使用quartz Spring自带的@Scheduled注解 简单的定时任务控制,注解使用spring自带定时任务就可以完成啦,定时任... 阅读详情

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

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

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

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

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

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

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

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

421ba338285229eb712957ca9607f4ba.png

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

此时库任务 Id 为7:

ad30fc0200b51b47f518c3fd54ba937c.png

控制台:

e0d3c01ecf5a24d5dc48cd589729b9c9.png

6.2 查询任务

435f8570b9692f35732f315d0cc2f9f7.png

6.3 编辑任务

d9bccb5b676d3c780d15f443d45c3a34.png

控制台输出:

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 来实现任务的动态管理,主要是代码,感兴趣的可以自行拿去验证改造并用于实践。

74bde8495fdf716ed0f2bf80aa28c322.png

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

cb2b632234ab354d9d38aa495062b857.png

ThreadPoolTaskScheduler实现动态管理定时任务 最近,有个项目有需要用到定时任务,所以做了一个动态管理定时任务的模块。本文将从项目背景、需求、选型、思路、具体实现等方面展开介绍。 背景:有个支付类的项目,中间会产生一些中间态的订单,需要有个定时任务轮询确认订单状态。该类项目体量较小,单节点部署,客户比较多,需要简单快速的部署、维护。 需求:定时任务能够通过表达式灵活指定执行计划,并支持动态启动、关闭、修改。定时任务模块最好和业务包在一个jar包内,部署简单。 选型:说到定时任务,当下最... 阅读详情

相关推荐

Spring Boot集成Quartz-动态任务管理

当定时任务越来越多时,集中管理Job越有必要。Quartz提供了一组API,来管理Job。

2025博客之星Top81。专注AI工程化与架构实战。从分布式思维到模型部署,用工程化视角为你厘清AI落地的真实路径。 8316

Spring Boot任务管理

Spring Boot任务管理异步任务无返回值异步任务调用有返回值异步任务调用定时任务定时任务介绍@EnableScheduliing@Scheduled 异步任务 Web应用开发中,大多数情况都是通过同步方式进行数据交换处理,当处理与第三方系统交互时,容易造成响应迟缓的情况,大部分都是使用多线程完成此类任务,除此之外,还可以使用异步调用的方式来解决这个问题 无返回值异步任务调用 示例: (1)创建一个Spring Boot项目,在Dependencies选择Web模块的Web依赖 (2)编写异步调用方法,

qq_50834939的博客 743

Spring Boot 实现定时任务

Spring Boot 实现定时任务

小李同学的博客 1889

SpringBoot框架中的DAO(mapper)层、Entity层、Service层、Controller层

在具体的项目中,其流程为:Controller层调用Service层的方法,Service层调用Dao(mapper)层中的方法,其中调用的参数是使用Entity层进行传递的。Controller层像是一个服务员,他把客人(前端)点的菜(数据、请求的类型等)进行汇总什么口味、咸淡、量的多少,交给厨师长(Service层),厨师长则告诉沾板厨师(Dao 1)、汤料房(Dao 2)、配菜厨师(Dao 3)等(统称Dao层)我需要什么样的半成品,副厨们(Dao层)就负责完成厨师长(Service)交代的任务。

后海大鲨鱼 1172

SpringBoot快速整合Quartz动态管理定时任务

SpringBoot快速整合Quartz动态管理定时任务 背景 ​ 如题,上个版本产品提出了一个报告需求的整体大功能,其中有一个小的子功能就是开发一个在页面==动态添加、删除、修改、启停==的定时任务来执行一系列的操作(生成周期报告、发送邮件等);关于SpringBoot中的定时任务,大家应该都比较熟悉,在主启动类上添加***@EnableScheduling***,然后直接在需要定时执行的方法上直接添加***@Scheduled(cron = “”)并且添加上定时任务的cron表达式即可;但是这种方式有一

weixin_43715518的博客 3786

Spring Boot中动态管理调度任务的实践指南

Spring Boot作为一个流行的Java框架,它简化了基于Spring的应用开发。Spring Boot核心思想是约定优于配置,提供了一种快速且简便的方式来创建独立的、生产级别的基于Spring的应用。它自动配置了大量常见的场景,极大地减少了项目搭建和配置的时间,让开发者能够专注于业务代码的编写。虽然Spring提供了默认的实现,但在某些场景下,你可能需要根据具体需求实现自定义的。创建自定义需要实现接口,并覆盖其中的方法来定义自己的调度逻辑。

weixin_35509395的博客 953

SpringBoot整合Spring Data JPA

Spring Data JPA 是更大的 Spring Data 家族的一部分,可以轻松实现基于 JPA 的存储库。该模块处理对基于 JPA 的数据访问层的增强支持。它使构建使用数据访问技术的 Spring 驱动的应用程序变得更加容易。

花伤情犹在的博客 1559

Altium Designer操作技巧(4)——如何利用嘉立创EDA来输出3D封装到AD封装库?

本文介绍了利用嘉立创EDA和FreeCAD软件为PCB元件添加3D模型的方法。首先在嘉立创EDA中搜索元件编号并导出3D模型,然后使用FreeCAD删除3D模型中的PCB基板部分,最后将处理后的3D模型导入PCB封装库并更新到PCB设计中。文章详细讲解了每个步骤的操作流程,包括模型导出、基板删除、位置调整等关键环节,并提供了解决常见问题的方法。通过这种方法,电子工程师可以快速获得元器件的3D模型,提高PCB设计的准确性。

u012526819的博客 2586

解析Java框架中entity层,mapper层,service层,controller各层作用

一、entity层 别名: model层 ,domain层 用途: 用于存放我们的实体类,与数据库中的属性值基本保持一致,实现set和get的方法。 例子:user表的实体user public class User { /** * 用户id */ @Id @GeneratedValue(strategy = GenerationType.IDENTIT...

互联网的修罗场 7万+

winpcap无法安装提示新版本已经安装解决办法

问题显示如下该问题会导致ensp里的云识别不到网卡可能是因为之前安装了npcap,而且安装时勾选了第四个选项导致无法安装,需要完全卸载npcap解决办法:办法一:在控制面板的程序与卸载里面卸载npcap和winPcap,然后再安装winPcap,如果搜不到往下看办法二:在C:\Windows\SysWOW64目录下搜索wpcap.dll和packet.dll并重命名为wpcap.dll.old和packet.dll.old办法三:如果搜不到这两个文件需要安装win10Pcap才能找到这两个文件下载地址:Wi

qq_73202377的博客 3万+

2025资源从哪里来!

研究网络通信中的安全问题,如VLAN划分、路由协议(静态、动态如OSPF、BGP)、NAT转换、AAA认证、访问控制列表(ACL)等。‌Wi-Fi安全设置‌、‌密码破解原理‌(仅用于合法测试或教育目的)、‌常见Web安全问题‌如SQL注入、XSS跨站脚本攻击等的防范措施。在这个网站你可以找到你对应的专业-对应的路线-对应的资源-对应的百度网盘里面全是免费的资源------------------惊喜不?‌防火墙配置与策略‌、‌入侵检测与防御系统(IDS/IPS)‌、‌端口扫描与漏洞扫描技术‌等。

qq_56248592的博客 1902

玩转 Spring Boot 集成篇(定时任务框架Quartz)

在日常项目研发中,定时任务可谓是必不可少的一环,关于 Spring Boot 如何实现静态定时任务、动态定时任务以及如何开启多线程跑任务,均已在上篇分享过,不再赘述。虽然 Spring B...

一猿小讲 7142

把数字化转型真正落到组织里:读懂 SAP 的 OCM 六维框架

摘要: SAP的变革管理框架(OCM)强调数字化转型不仅是技术任务,更需关注组织与人的适应性。OCM包含六大维度:变革战略(明确方向与风险)、变革领导力(统一关键管理者行动)、变革沟通(分层传递信息)、变革实现(细化岗位级影响)、变革赋能(按角色设计培训)和变革有效性(持续评估)。该框架贯穿项目全周期,尤其在云项目中,需从早期规划变革策略,避免后期高代价调整。OCM的核心是将系统设计转化为组织语言,确保业务真正采纳新方式运作。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1433

JavaWeb29-Spring Boot:定时任务动态管理(新增 / 暂停 / 删除任务)

动态定时任务管理系统实现方案 本文基于Spring Boot构建了一个完整的动态定时任务管理系统,解决了原生@Scheduled注解无法运行时修改的痛点。系统核心架构包含: 任务持久化层:使用H2数据库存储任务配置(cron表达式、执行类、状态等) 任务调度引擎:基于ThreadPoolTaskScheduler实现任务的动态注册与取消 统一执行接口:通过TaskExecutor规范任务执行逻辑 状态管理:支持任务的启动、暂停、恢复、删除等操作 关键实现包括: 动态加载数据库中的任务配置 线程安全的并发任务

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

2026滑块验证码终极实战:缺口检测算法+人机轨迹模拟,识别率从60%飙升至95%全方案

**摘要:2026年AIGC技术发展导致传统验证码失效,滑块验证码成为主流防护手段。本文深入分析了传统方案识别率低的原因,并提出多级检测融合和行为拟合优化方案。通过图像预处理优化、三级缺口检测引擎(差分图像法、Hu矩轮廓匹配、YOLOv8n模型兜底)将识别率提升至99.2%。同时针对轨迹模拟环节,设计了包含抖动特征、时间随机性和个体差异的行为模型,使通过率从12%提升至87%。该方案解决了传统方法在缺口定位精度和轨迹真实性方面的核心痛点,为应对新一代验证码风控提供了完整的技术框架。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1899

3. UFS 电气时钟、复位、信号和电源

当接收到用于从 HS-MODE 到 LS-MODE 转换的 DME_POWERMODE.ind 或用于从 HS-MODE 到 HIBERN8 转换的 DME_HIBERNATE_ENTER.ind 时,UFS 主机可以启动一个计时器。在整个 PA_INIT 过程中,不应门控参考时钟。如果 UFS 主机启动从 HS-MODE 到 LS-MODE 或 HIBERN8 的电源模式更改,则应确保在关闭参考时钟之前,至少已经经过了由 bRefClkGatingWaitTime 定义的最小时间间隔,见图 6.3。

qq_42238448的博客 1293

spring boot 集成Quartz 动态管理定时任务

spring boot 集成Quartz 动态管理定时任务 简介 Quartz 是一款强大的任务调度器,可以实现较为复杂的调度功能,并且支持集群。 spring boot项目中集成Quartz 1.添加maven依赖 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <v

qq_40464334的博客 395

整车电检工艺流程

本文是笔者在从事汽车行业智能制造领域过程,对于整车电检工艺流程的理解与总结。

qq_40664711的博客 1万+

Spring boot + quartz 实现动态管理任务

在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合实现动态的改变定时任务的状态的一个实现。 配置文件 相关jar包依赖配置 这里使用的 gradle 进行依赖管理。包含了 jpa, quartz, thymeleaf...

我飞故我在的专栏 7209

基于Simulink的电源纹波抑制仿真

电源纹波(Power Supply Ripple)是指直流电源输出中存在的交流分量。纹波的存在会导致电压波动,进而影响负载设备的性能和寿命。为了确保高质量的电源输出,通常需要设计有效的滤波电路来抑制纹波。Simulink 提供了一个强大的仿真环境,可以用来设计、分析和验证电源纹波抑制方案。本教程将详细介绍如何使用 Simulink 进行电源纹波抑制的仿真实例。通过本教程,我们详细介绍了如何使用 Simulink 进行电源纹波抑制的仿真实例。背景介绍:理解电源纹波及其对电力电子系统的影响。仿真建模过程。

xiaoheshang_123的博客 513

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

动态定时任务在Spring Boot中集成Quartz的实践

在现代软件开发中,定时任务是一个常见的需求,比如定时备份数据、定时发送邮件等。Spring Boot提供了对Quartz定时任务的集成支持,使得我们可以轻松地在Spring Boot应用中实现定时任务。本文将介绍如何在Spring Boot中集成Quartz,并实现动态定时任务。

心猿意码 1117

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

重学SpringBoot3-整合Quartz定时任务

Quartz 是一个开源的任务调度框架,用于在应用程序中创建、管理和调度定时任务。将 Quartz 和 Spring Boot 3 结合,可以轻松实现定时任务的灵活管理。本文将详细介绍如何在 Spring Boot 3 项目中集成 Quartz Scheduler 并实现示例任务的调度。

CoderJia的学习之路 5518

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Quartz+Spring Boot实现动态管理定时任务

在实践中遇到动态管理定时任务的需求,场景通常是动态添加、更新、删除任务,借助Quartz,可方便实现功能。 以下使用Quartz结合Spring Boot方式使用。 POM依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-bo...

早起的鸟儿有虫吃的博客 688

Spring Boot 实现定时任务动态管理,太爽了!

SpringBoot的定时任务的加强工具,实现对SpringBoot原生的定时任务进行动态管理,完全兼容原生@Scheduled注解,无需对原本的定时任务进行修改。版权声明:本文为优快云博主「敲代码的旺财」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。增强器实现的整体思路与SpringAop的思路一致,实现没有Aop复杂。(2) 使用后处理器拦截SpringBoot原本的定时任务。(4) cglib动态代理实现。(5) 定时任务执行器实现。(6) 增强器代理逻辑。

Java技术栈,分享最主流的Java技术 1764

Spring Boot-定时任务问题

Spring Boot 提供了简单而强大的定时任务管理机制,但在实际开发中可能遇到任务调度不准确、任务并发冲突、任务失败等问题。通过合理的配置和优化,开发者可以有效解决这些问题。对于更复杂的调度需求,Spring Boot 还可以通过集成 Quartz 等框架来实现。

Flying_Fish_roe的博客 3268
上一篇: 玩转 Spring Boot 集成篇(定时任务框架Quartz)
下一篇: 玩转 Spring Boot 原理篇(源码环境搭建)
一猿小讲
博客等级 码龄15年 294粉丝 156原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值