Springboot项目,动态配置定时任务代码示例
一、基于ThreadPoolTaskScheduler动态配置定时任务
ThreadPoolTaskScheduler
是 Spring 框架中的一个特殊的 TaskScheduler
实现,它使用线程池来执行定时任务。
在 Spring 框架中,TaskScheduler
接口提供了一种方便的方式来执行定时任务。ThreadPoolTaskScheduler
是 TaskScheduler
接口的一个具体实现,它使用线程池来管理任务的执行。使用线程池可以有效地管理任务的执行,避免每个任务都创建一个新的线程,从而降低了系统的开销。
ThreadPoolTaskScheduler
具有以下特点:
- 线程池管理: 它使用一个线程池来执行任务,可以配置线程池的大小、线程池的类型(如固定大小的线程池、可缓存的线程池等)等参数。
- 并发执行: 它可以同时执行多个任务,提高了任务的并发性能。
- 任务调度: 它支持在指定的时间间隔、固定的延迟或者特定的时间点执行任务,提供了灵活的任务调度功能。
- 异常处理: 它提供了异常处理机制,可以处理任务执行过程中抛出的异常,保证任务的稳定执行。
- Spring 集成: 作为 Spring 框架的一部分,
ThreadPoolTaskScheduler
可以与其他 Spring 组件(如@Scheduled
注解、TaskExecutor
接口等)无缝集成,方便地管理和调度任务。
使用 ThreadPoolTaskScheduler
可以轻松地实现在 Spring 应用中的定时任务调度,提高了系统的可靠性和性能。
示例代码:
java
package com.jlstest.threadPoolTaskSchedulerDemo.service;
import com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity;
/**
* @className: CronService
* @Description: TODO
* @author: JLS
* @date: 2023/2/3 9:39
*/
public interface CronService {
/**
* 开始定时任务
*
* @param cron
* 定时任务信息
*/
void startCron(CronEntity cron);
/**
* 结束定时任务
*
* @param cron
* 定时任务信息
*/
void stopCron(CronEntity cron);
/**
* 修改定时任务
*/
void changeCron(CronEntity cron);
}
java
package com.jlstest.threadPoolTaskSchedulerDemo.service.impl;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.example.common.exception.ServiceException;
import com.jlstest.threadPoolTaskSchedulerDemo.dao.CronManagementDao;
import com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity;
import com.jlstest.threadPoolTaskSchedulerDemo.service.CronService;
import com.jlstest.threadPoolTaskSchedulerDemo.utils.CronUtils;
import lombok.extern.slf4j.Slf4j;
/**
* @author JLS
* @description:
* @since 2023-02-03 09:40
*/
@Service
@Slf4j
public class CronServiceImpl implements CronService {
@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Resource
private CronManagementDao cronManagementDao;
private final Map<Long, ScheduledFuture<?>> futureMap = new HashMap<>();
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
// 初始化,当服务重启则重启对应的定时任务
@PostConstruct
public void init() {
List<CronEntity> cronEntityList = cronManagementDao.selectCronManagementAll();
// 校验是否有定时任务,如果有则启动
if (CollectionUtils.isEmpty(cronEntityList)) {
return;
}
// 遍历每个定时任务
cronEntityList.forEach(cron -> {
if (LocalDateTime.now().isAfter(cron.getDeadTime())) {
cronManagementDao.deleteCronManagement(cron.getId());
log.info("删除过期定时任务成功,任务id:{},任务标题:{},任务开始时间:{},任务截止时间:{}", cron.getId(), cron.getTitle(), cron.getStartTime(), cron.getDeadTime());
} else {
// 启动任务
startCron(cron);
}
});
}
/**
* 开始定时任务
*
* @param cron
* 定时任务信息
*/
@Override
public void startCron(CronEntity cron) {
if (futureMap.containsKey(cron.getId())) {
log.warn("已经存在重复任务,任务id:{},任务标题:{},任务开始时间:{},任务截止时间:{}", cron.getId(), cron.getTitle(), cron.getStartTime(), cron.getDeadTime());
return;
}
// 校验cron表达式是否合法
if (CronUtils.isValid(cron.getCronExp())) {
log.error("cron表达式不合法,报文:{}", JSON.toJSONString(cron));
throw new ServiceException("cron表达式不合法");
}
// 校验时间,通过后开始设置定时任务
if (LocalDateTime.now().isBefore(cron.getDeadTime()) || cron.getDeadTime() == null) {
// 校验cron表达式是否合法
CronUtils.isValid(cron.getCronExp());
ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(new MyRunnable(cron), new CronTrigger(cron.getCronExp()));
// 插入数据库
if (cron.getId() == null) {
cronManagementDao.insertCronManagement(cron);
}
futureMap.put(cron.getId(), future);
log.info("启动定时任务成功,任务id:{},任务标题:{},任务开始时间:{},任务截止时间:{}", cron.getId(), cron.getTitle(), cron.getStartTime(), cron.getDeadTime());
}
}
/**
* 结束定时任务
*
* @param cron
* 定时任务信息
*/
@Override
public void stopCron(CronEntity cron) {
ScheduledFuture<?> future = futureMap.get(cron.getId());
if (future != null) {
future.cancel(true);
futureMap.remove(cron.getId());
log.info("关闭定时任务成功,任务id:{},任务标题:{},任务开始时间:{},任务截止时间:{}", cron.getId(), cron.getTitle(), cron.getStartTime(), cron.getDeadTime());
cronManagementDao.deleteCronManagement(cron.getId());
}
}
/**
* 修改定时任务
*
* @param cron
* 定时任务信息
*/
@Override
public void changeCron(CronEntity cron) {
// 先停止,在开启.
stopCron(cron);
cron.setId(null);
startCron(cron);
}
private class MyRunnable implements Runnable {
private CronEntity cron;
public MyRunnable(CronEntity cron) {
this.cron = cron;
}
@Override
public void run() {
// 检查当前时间是否已经到了任务的截止时间
if (LocalDateTime.now().isEqual(cron.getDeadTime()) || LocalDateTime.now().isAfter(cron.getDeadTime())) {
// 如果已经到了截止时间,那么停止任务
stopCron(cron);
}
// 校验当前时间是否已经到了任务指定的开始执行时间,没到则不指定定时任务
if (LocalDateTime.now().isBefore(cron.getStartTime())) {
log.info("任务还未到开始时间,任务id:{}", cron.getTitle());
return;
}
// 定义任务要做的事,完成任务逻辑
log.info("定时任务测试,定时任务id:{},当前时间为:{}", cron.getTitle(), LocalDateTime.now());
// todo 可以根据任务id编写对应的定时任务逻辑
}
}
public static void main(String[] args) {
System.out.println(LocalDateTime.now());
}
}
代码较为简单,主要包括了新建定时任务,暂停定时任务,修改定时任务的功能;
实体类:
java
package com.jlstest.threadPoolTaskSchedulerDemo.entity;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author JLS
* @description:
* @since 2023-02-03 09:32
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class CronEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long id;
/**
* 标题
*/
private String title;
/**
* 截止时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime deadTime;
/**
* 开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;
/**
* cron表达式
*/
private String cronExp;
}
数据库操作方法:
java
package com.jlstest.threadPoolTaskSchedulerDemo.dao;
import com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author JLS
* @description:
* @since 2024-02-19 14:41
*/
@Mapper
public interface CronManagementDao {
/**
* 新增定时任务
*/
void insertCronManagement(CronEntity cronEntity);
/**
* 修改定时任务
*/
int updateCronManagement(CronEntity cronEntity);
/**
* 删除定时任务
*/
int deleteCronManagement(Long id);
/**
* 查询定时任务
*/
CronEntity selectCronManagement(Long id);
/**
* 查询所有定时任务
*/
List<CronEntity> selectCronManagementAll();
}
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.jlstest.threadPoolTaskSchedulerDemo.dao.CronManagementDao">
<resultMap id="map" type="com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="dead_time" property="deadTime"/>
<result column="start_time" property="startTime"/>
<result column="cron_exp" property="cronExp"/>
</resultMap>
<select id="selectCronManagementAll" resultMap="map">
select * from cron_management
</select>
<select id="selectCronManagement" resultMap="map">
select * from cron_management where id = #{id}
</select>
<insert id="insertCronManagement" parameterType="com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity" useGeneratedKeys="true" keyProperty="id">
insert into cron_management (title, dead_time, start_time, cron_exp) values (#{title}, #{deadTime}, #{startTime}, #{cronExp})
</insert>
<update id="updateCronManagement" parameterType="com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity">
update cron_management
<set>
<if test="title != null and title != ''">
title = #{title},
</if>
<if test="deadTime != null">
dead_time = #{deadTime},
</if>
<if test="startTime != null">
start_time = #{startTime},
</if>
<if test="cronExp != null and cronExp != ''">
cron_exp = #{cronExp},
</if>
</set>
where id = #{id}
</update>
<delete id="deleteCronManagement" parameterType="Long">
delete from cron_management where id = #{id}
</delete>
</mapper>
<!-- 数据库建表语句
create table if not exists cron_management
(
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键id',
title VARCHAR(255) COMMENT '标题',
dead_time datetime COMMENT '截止时间',
start_time datetime COMMENT '开始时间',
cron_exp VARCHAR(255) COMMENT 'cron表达式',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
comment 'cron任务信息调度记录表';
-->
具体的实际使用示例:
kotlin
package com.jlstest.threadPoolTaskSchedulerDemo.controller;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.common.response.JlsTestResponse;
import com.jlstest.threadPoolTaskSchedulerDemo.entity.CronEntity;
import com.jlstest.threadPoolTaskSchedulerDemo.service.CronService;
/**
* @author JLS
* @description:
* @since 2024-02-20 10:16
*/
@RestController
@RequestMapping("/threadPoolTaskTest")
public class ThreadPoolTaskTestController {
@Resource
private CronService cronService;
/**
* 新建定时任务
*/
@PutMapping("/addCron")
public JlsTestResponse<Object> addCron(@RequestBody CronEntity cronEntity) {
// 新建定时任务
cronService.startCron(cronEntity);
return JlsTestResponse.sendSuccess("新建定时任务成功");
}
/**
* 停止定时任务
*/
@DeleteMapping("/stopCron")
public JlsTestResponse<Object> stopCron(@RequestBody CronEntity cronEntity) {
// 停止定时任务
cronService.stopCron(cronEntity);
return JlsTestResponse.sendSuccess("停止定时任务成功");
}
/**
* 修改定时任务
*/
@PostMapping("/updateCron")
public JlsTestResponse<Object> updateCron(@RequestBody CronEntity cronEntity) {
// 修改定时任务
cronService.changeCron(cronEntity);
return JlsTestResponse.sendSuccess("修改定时任务成功");
}
}
1.定时间段定时任务示例:
接口:/threadPoolTaskTest/addCron
请求参数:
json
{
"title": "定时任务测试1",
"deadTime": "2024-02-20 10:51:00",
"startTime": "2024-02-20 10:50:00",
"cronExp": "* * * * *"
}
设置定时任务,名称为定时任务测试1,从2024-02-20 14:50:00开始执行定时任务2024-02-20 14:51:00终止定时任务,期间每秒执行一次
,控制台显示的内容
开始执行定时任务:
定时任务执行结束:
2.其他定时任务功能展示
开始执行定时任务:
数据库内容展示:
然后在接口: /threadPoolTaskTest/stopCron
参数:
json
{
"id": 23,
"title": "定时任务测试1",
"deadTime": "2024-02-21 10:36:59",
"startTime": "2024-02-20 10:36:59",
"cronExp": "* * * * * *"
}
暂停定时任务
接口返回:
控制台显示,结束后数据库的定时任务也会删除,
总结
实际应用中,也可以根据业务需要设置对应的定时任务状态以及其他的一些改造。
二、基于quartz包的动态配置定时任务
Quartz 是一个功能强大的开源调度框架,它允许你基于 Cron 表达式或者固定的时间间隔执行任务。使用 Quartz,你可以轻松地实现动态配置和管理定时任务。
scheduleTask
方法用于添加新的定时任务,它接受任务的唯一标识符(taskId)、任务类(实现了Job
接口的类)和 Cron 表达式作为参数,并将任务调度添加到 Quartz 调度器中。updateTask
方法用于更新现有的定时任务,它首先获取任务的触发器(Trigger),然后更新触发器的 Cron 表达式,最后重新调度任务。removeTask
方法用于移除指定的定时任务,它暂停任务的触发器、移除触发器,并删除任务。
你需要在 Spring 应用中将这个类作为一个组件(Component)进行管理,这样就可以通过注入的方式来使用它,并在需要的时候添加、更新或删除定时任务。
示例代码
1.config包下
kotlin
package com.jlstest.sysJobDemo.config;
import org.quartz.Scheduler;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author JLS
* @description:
* @since 2024-02-20 11:18
*/
@Configuration
public class QuartzConfiguration {
@Bean
public Scheduler scheduler() throws Exception {
return StdSchedulerFactory.getDefaultScheduler();
}
}
2.constant包下
arduino
package com.jlstest.sysJobDemo.constant;
/**
* @author JLS
* @description:
* @since 2024-02-18 15:00
*/
public class ScheduleConstants {
public static enum Status {
/**
* 正常
*/
NORMAL("0"),
/**
* 暂停
*/
PAUSE("1");
private final String value;
Status(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public static final String TASK_PROPERTIES = "job";
}
3.dao包下
java
package com.jlstest.sysJobDemo.dao;
import com.jlstest.sysJobDemo.entity.SysJob;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author JLS
* @description:
* @since 2024-02-18 15:32
*/
@Mapper
public interface SysJobMapper {
List<SysJob> selectJobAll();
List<SysJob> selectJobList(SysJob job);
SysJob selectJobById(Long jobId);
int updateJob(SysJob job);
int deleteJobById(Long jobId);
int insertJob(SysJob job);
}
对应的xml:
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.jlstest.sysJobDemo.dao.SysJobMapper">
<resultMap id="sysJobResultMap" type="com.jlstest.sysJobDemo.entity.SysJob">
<result column="job_id" property="jobId" />
<result column="job_name" property="jobName" />
<result column="job_group" property="jobGroup" />
<result column="method_name" property="methodName" />
<result column="method_params" property="methodParams" />
<result column="cron_expression" property="cronExpression" />
<result column="status" property="status" />
<result column="remark" property="remark" />
<result column="create_by" property="createBy" />
<result column="create_time" property="createTime" />
<result column="update_by" property="updateBy" />
<result column="update_time" property="updateTime" />
</resultMap>
<select id="selectJobAll" resultMap="sysJobResultMap">
select * from sys_job
</select>
<select id="selectJobList" resultMap="sysJobResultMap">
select * from sys_job
<where>
<if test="jobName != null and jobName != ''">
and job_name like concat('%',#{jobName},'%')
</if>
<if test="jobGroup != null and jobGroup != ''">
and job_group = #{jobGroup}
</if>
<if test="status != null and status != ''">
and status = #{status}
</if>
</where>
</select>
<select id="selectJobById" resultMap="sysJobResultMap">
select * from sys_job where job_id = #{jobId}
</select>
<update id="updateJob">
update sys_job
<set>
<if test="jobName != null and jobName != ''">
job_name = #{jobName},
</if>
<if test="jobGroup != null and jobGroup != ''">
job_group = #{jobGroup},
</if>
<if test="methodName != null and methodName != ''">
method_name = #{methodName},
</if>
<if test="methodParams != null and methodParams != ''">
method_params = #{methodParams},
</if>
<if test="cronExpression != null and cronExpression != ''">
cron_expression = #{cronExpression},
</if>
<if test="status != null and status != ''">
status = #{status},
</if>
<if test="remark != null and remark != ''">
remark = #{remark},
</if>
<if test="updateBy != null and updateBy != ''">
update_by = #{updateBy},
</if>
update_time = now()
</set>
where job_id = #{jobId}
</update>
<delete id="deleteJobById">
delete from sys_job where job_id = #{jobId}
</delete>
<insert id="insertJob">
insert into sys_job (job_name, job_group, method_name, method_params, cron_expression, status, remark, create_by, create_time, update_by, update_time)
values (#{jobName}, #{jobGroup}, #{methodName}, #{methodParams}, #{cronExpression}, #{status}, #{remark}, #{createBy}, now(), #{updateBy}, now())
</insert>
<!-- 建表语句:
create table if not exists sys_job
(
job_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '任务ID',
job_name VARCHAR(100) NOT NULL COMMENT '任务名称',
job_group VARCHAR(100) NOT NULL COMMENT '任务组名',
method_name VARCHAR(100) NOT NULL COMMENT '任务方法',
method_params VARCHAR(255) COMMENT '方法参数',
cron_expression VARCHAR(100) NOT NULL COMMENT 'CRON表达式',
status CHAR(1) NOT NULL COMMENT '任务状态(0正常 1暂停)',
remark VARCHAR(255) COMMENT '备注',
create_by VARCHAR(50) COMMENT '创建者',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(50) COMMENT '更新者',
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
comment 'job任务调度信息记录表';
-->
</mapper>
4.entity实体类
typescript
package com.jlstest.sysJobDemo.entity;
import java.io.Serializable;
import java.util.Date;
/**
* @author JLS
* @description:
* @since 2024-02-18 14:44
*/
public class SysJob implements Serializable {
private static final long serialVersionUID = 1L;
/** 任务ID */
private Long jobId;
/** 任务名称 */
private String jobName;
/** 任务组名 */
private String jobGroup;
/** 任务方法 */
private String methodName;
/** 方法参数 */
private String methodParams;
/** CRON表达式 */
private String cronExpression;
/** 任务状态(0正常 1暂停) */
private String status;
/** 备注 */
private String remark;
/** 创建者 */
private String createBy;
/** 创建时间 */
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
private Date updateTime;
// Getters and setters
public Long getJobId() {
return jobId;
}
public void setJobId(Long jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getMethodParams() {
return methodParams;
}
public void setMethodParams(String methodParams) {
this.methodParams = methodParams;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
5.service包
arduino
package com.jlstest.sysJobDemo.service;
import com.jlstest.sysJobDemo.entity.SysJob;
import org.quartz.SchedulerException;
import java.util.List;
/**
* @author JLS
* @description:
* @since 2024-02-18 14:43
*/
public interface SysJobService {
/**
* 获取quartz调度器的计划任务列表
*/
List<SysJob> selectJobList(SysJob job);
/**
* 通过调度任务ID查询调度信息
*/
SysJob selectJobById(Long jobId);
/**
* 暂停任务
*/
int pauseJob(SysJob job) throws SchedulerException;
/**
* 恢复任务
*/
int resumeJob(SysJob job) throws SchedulerException;
/**
* 删除任务后,所对应的trigger也将被删除
*/
int deleteJob(SysJob job) throws SchedulerException;
/**
* 批量删除调度信息
*/
void deleteJobByIds(Long[] jobIds) throws SchedulerException;
/**
* 任务调度状态修改
*/
int changeStatus(SysJob job) throws SchedulerException;
/**
* 立即运行任务
*/
boolean run(SysJob job) throws SchedulerException;
/**
* 新增任务
*/
int insertJob(SysJob job) throws SchedulerException;
/**
* 更新任务的时间表达式
*/
int updateJob(SysJob job) throws SchedulerException;
/**
* 更新任务
*/
void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException;
/**
* 校验cron表达式是否有效
*/
boolean checkCronExpressionIsValid(String cronExpression);
}
ini
package com.jlstest.sysJobDemo.service.Impl;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.jlstest.sysJobDemo.constant.ScheduleConstants;
import com.jlstest.sysJobDemo.dao.SysJobMapper;
import com.jlstest.sysJobDemo.entity.SysJob;
import com.jlstest.sysJobDemo.service.SysJobService;
import com.jlstest.sysJobDemo.utils.CronUtils;
import com.jlstest.sysJobDemo.utils.ScheduleUtils;
/**
* @author JLS
* @description:
* @since 2024-02-18 14:54
*/
@Service
public class SysJobServiceImpl implements SysJobService {
@Resource
private Scheduler scheduler;
@Resource
private SysJobMapper jobMapper;
/**
* 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
*/
@PostConstruct
public void init() throws SchedulerException {
scheduler.clear();
// 启动调度器 这个一定要写
scheduler.start();
List<SysJob> jobList = jobMapper.selectJobAll();
for (SysJob job : jobList) {
ScheduleUtils.createScheduleJob(scheduler, job);
}
}
/**
* 获取quartz调度器的计划任务列表
*
* @param job
* 调度信息
* @return
*/
@Override
public List<SysJob> selectJobList(SysJob job) {
return jobMapper.selectJobList(job);
}
/**
* 通过调度任务ID查询调度信息
*
* @param jobId
* 调度任务ID
* @return 调度任务对象信息
*/
@Override
public SysJob selectJobById(Long jobId) {
return jobMapper.selectJobById(jobId);
}
/**
* 暂停任务
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int pauseJob(SysJob job) throws SchedulerException {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
System.out.println("JobKey: " + jobKey);
scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
System.out.println("Trigger state: " + triggerState);
}
// 获取作业的状态
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail != null) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
System.out.println("Trigger state for job " + jobKey + ": " + triggerState);
}
} else {
System.out.println("Job " + jobKey + " not found.");
}
return rows;
}
/**
* 恢复任务
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int resumeJob(SysJob job) throws SchedulerException {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
System.out.println("JobKey: " + jobKey);
scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
System.out.println("Trigger state: " + triggerState);
}
return rows;
}
/**
* 删除任务后,所对应的trigger也将被删除
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteJob(SysJob job) throws SchedulerException {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
int rows = jobMapper.deleteJobById(jobId);
if (rows > 0) {
scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
}
return rows;
}
/**
* 批量删除调度信息
*
* @param jobIds
* 需要删除的任务ID
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
for (Long jobId : jobIds) {
SysJob job = jobMapper.selectJobById(jobId);
deleteJob(job);
}
}
/**
* 任务调度状态修改
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int changeStatus(SysJob job) throws SchedulerException {
int rows = 0;
String status = job.getStatus();
if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
rows = resumeJob(job);
} else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
rows = pauseJob(job);
}
return rows;
}
/**
* 立即运行任务
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean run(SysJob job) throws SchedulerException {
boolean result = false;
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
SysJob properties = selectJobById(job.getJobId());
// 参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
result = true;
scheduler.triggerJob(jobKey, dataMap);
}
return result;
}
/**
* 新增任务
*
* @param job
* 调度信息 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertJob(SysJob job) throws SchedulerException {
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
// 写入数据库
int rows = jobMapper.insertJob(job);
// 写入成功后开始添加定时任务
if (rows > 0) {
ScheduleUtils.createScheduleJob(scheduler, job);
}
return rows;
}
/**
* 更新任务的时间表达式
*
* @param job
* 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int updateJob(SysJob job) throws SchedulerException {
SysJob properties = selectJobById(job.getJobId());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
updateSchedulerJob(job, properties.getJobGroup());
}
return rows;
}
/**
* 更新任务
*
* @param job
* 任务对象
* @param jobGroup
* 任务组名
*/
public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException {
Long jobId = job.getJobId();
// 判断是否存在
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
ScheduleUtils.createScheduleJob(scheduler, job);
}
/**
* 校验cron表达式是否有效
*
* @param cronExpression
* 表达式
* @return 结果
*/
@Override
public boolean checkCronExpressionIsValid(String cronExpression) {
return CronUtils.isValid(cronExpression);
}
}
6.具体的任务:
java
package com.jlstest.sysJobDemo.task;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* @author JLS
* @description:
* @since 2024-02-18 14:50
*/
public class Task1Job implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 编写任务1的具体逻辑
System.out.println("任务1执行中...");
}
}
7.utils工具包
typescript
package com.jlstest.sysJobDemo.utils;
import org.quartz.CronExpression;
/**
* @author JLS
* @description:
* @since 2024-02-18 15:17
*/
public class CronUtils {
/**
* 验证给定的CRON表达式是否有效
*
* @param cronExpression
* CRON表达式
* @return 如果有效返回true,否则返回false
*/
public static boolean isValid(String cronExpression) {
try {
// 使用Quartz提供的CronExpression类解析CRON表达式
new CronExpression(cronExpression);
return true;
} catch (Exception e) {
// 解析失败则说明不是有效的CRON表达式
return false;
}
}
}
java
package com.jlstest.sysJobDemo.utils;
import com.jlstest.sysJobDemo.entity.SysJob;
import com.jlstest.sysJobDemo.task.Task1Job;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
/**
* @author JLS
* @description:
* @since 2024-02-18 14:46
*/
public class ScheduleUtils {
/**
* 创建定时任务
*
* @param scheduler
* 调度器
* @param job
* 定时任务信息
* @throws SchedulerException
*/
public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException {
// 构建定时任务的触发器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).build();
// 构建定时任务的JobDetail 如果有多种任务选择,则根据不同任务创建不同的JobDetail
JobDetail jobDetail = JobBuilder.newJob(Task1Job.class).withIdentity(job.getJobId().toString(), job.getJobGroup()).build();
// 将任务信息设置到JobDataMap中,传递给任务的执行方法
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.put("job", job);
// 调度器中添加触发器和JobDetail
scheduler.scheduleJob(jobDetail, trigger);
}
/**
* 根据任务ID和任务组名创建唯一的JobKey
*
* @param jobId
* 任务ID
* @param jobGroup
* 任务组名
* @return 创建的JobKey对象
*/
public static JobKey getJobKey(Long jobId, String jobGroup) {
return new JobKey(String.valueOf(jobId), jobGroup);
}
}
8.pom中的依赖
在pom中记得要加上依赖
xml
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.5.0-rc1</version>
</dependency>
具体代码的作用代码注释中都有写。
9.测试用controller
kotlin
package com.jlstest.sysJobDemo.controller;
import javax.annotation.Resource;
import org.quartz.SchedulerException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.common.response.JlsTestResponse;
import com.jlstest.sysJobDemo.entity.SysJob;
import com.jlstest.sysJobDemo.service.SysJobService;
/**
* @author JLS
* @description:
* @since 2024-02-20 10:16
*/
@RestController
@RequestMapping("/sysJobTest")
public class SysJobTestController {
@Resource
private SysJobService sysJobService;
/**
* 新增任务
*/
@PutMapping("/insertJob")
public JlsTestResponse<Object> insertJob(@RequestBody SysJob job) throws SchedulerException {
int i = sysJobService.insertJob(job);
if (i > 0) {
return JlsTestResponse.sendSuccess("新增成功");
} else {
return JlsTestResponse.sendFailure("新增失败");
}
}
/**
* 暂停任务
*/
@PostMapping("/pauseJob")
public JlsTestResponse<Object> pauseJob(@RequestBody SysJob job) throws SchedulerException {
int i = sysJobService.pauseJob(job);
if (i > 0) {
return JlsTestResponse.sendSuccess("暂停成功");
} else {
return JlsTestResponse.sendFailure("暂停失败");
}
}
/**
* 恢复任务
*/
@PostMapping("/resumeJob")
public JlsTestResponse<Object> resumeJob(@RequestBody SysJob job) throws SchedulerException {
int i = sysJobService.resumeJob(job);
if (i > 0) {
return JlsTestResponse.sendSuccess("恢复成功");
} else {
return JlsTestResponse.sendFailure("恢复失败");
}
}
/**
* 删除任务
*/
@DeleteMapping("/deleteJob")
public JlsTestResponse<Object> deleteJob(@RequestBody SysJob job) throws SchedulerException {
int i = sysJobService.deleteJob(job);
if (i > 0) {
return JlsTestResponse.sendSuccess("删除成功");
} else {
return JlsTestResponse.sendFailure("删除失败");
}
}
}
注意要点:
要注意创建任务的时候,选用的主键要拎清楚,也就是构建JobDetail这一步,代码在
utils.ScheduleUtils#createScheduleJob
中,不然的会容易导致任务删除,启用,停用功能失效,
这个主要是测试暂停和恢复的功能,会打印作业的主键值以及对应作业的触发器状态
Trigger state: PAUSED 为暂停,说明定时任务停止
Trigger state: NORMAL 为正常,说明任务正常执行中
三、总结:
以上的代码均为简单的demo,对于不同业务可以有不同的拓展,
具体代码哪一步怎么使用,代码注释中基本都有涵盖