# boot-quartz
**Repository Path**: liang-tian-yu/boot-quartz
## Basic Information
- **Project Name**: boot-quartz
- **Description**: quartz
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-06-26
- **Last Updated**: 2025-06-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## spring-boot-starter-quartz
### 设计
- 系统表设计
```
create table sys_quartz_job
(
id varchar(32) comment 'id' primary key,
jobClass varchar(64) not null comment '任务类名',
cron varchar(64) not null comment 'cron表达式',
params varchar(512) null comment '参数',
description varchar(128) null comment '任务描述',
status tinyint default 0 not null comment '任务状态 0-正常 1-暂停',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)
comment '定时任务';
```
方法设计
- 查询定时任务
- 添加定时任务
- 更新定时任务
- 暂停定时任务
- 恢复定时任务
- 删除定时任务
- 立即执行一次任务
- 启动后应该自动将正常任务注册
添加定时任务->暂停定时任务->立即执行一次任务->恢复定时任务->->删除定时任务 (测试流程)
---
### 代码实现
- 添加依赖
```
org.springframework.boot
spring-boot-starter-quartz
```
- 配置
```
spring:
# 定时任务
quartz:
# 任务信息存储至数据库
job-store-type: jdbc
jdbc:
# 自动生成表,若已有表数据请务必关闭,第一次可选ALWAYS(ALWAYS/EMBEDDED/NEVER)
initialize-schema: NEVER
properties:
org:
quartz:
scheduler:
# 允许调度程序节点一次获取(触发)的最大触发器数
batchTriggerAcquisitionMaxCount: 5
jobStore:
# 加锁调度
acquireTriggersWithinLock: true
# “容忍”触发器经过下一次触发时间的毫秒数
misfireThreshold: 10000
```
- 启动类加
```
@EnableScheduling
```
- 实体类
```
package com.lty.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 定时任务
*/
@Data
@TableName("sys_quartz_job")
public class QuartzJob {
/**
* id
*/
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/**
* 任务类名
*/
private String jobClass;
/**
* Cron表达式
*/
private String cron;
/**
* 参数
*/
private String params;
/**
* 任务描述
*/
private String description;
/**
* 任务状态(0-正常,1-暂停)
*/
private Integer status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
```
- 常量
```
package com.lty.constant;
/**
* 定时任务常量
*/
public class QuartzJobConstant {
/**
* 定时任务状态-正常
*/
public static final Integer JOB_STATUS_NORMAL = 0;
/**
* 定时任务状态-暂停
*/
public static final Integer JOB_STATUS_PAUSED = 1;
/**
* 定时任务组-默认
*/
public static final String JOB_GROUP_DEFAULT = "JOB_GROUP";
/**
* 定时任务参数-任务参数key
*/
public static final String JOB_PARAMS_KEY = "params";
}
```
MapperXML
```
```
Mapper
```
package com.lty.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lty.model.entity.QuartzJob;
public interface QuartzJobMapper extends BaseMapper {
}
```
Service
```
package com.lty.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.lty.model.entity.QuartzJob;
import org.quartz.SchedulerException;
public interface QuartzJobService extends IService {
/**
* 添加定时任务
*/
void addJob(QuartzJob job) throws SchedulerException;
/**
* 更新定时任务
*/
void updateJob(QuartzJob job) throws SchedulerException;
/**
* 删除定时任务
*/
void deleteJob(String jobId) throws SchedulerException;
/**
* 暂停定时任务
*/
void pauseJob(String jobId) throws SchedulerException;
/**
* 恢复定时任务
*/
void resumeJob(String jobId) throws SchedulerException;
/**
* 立即执行定时任务
*/
void runJobNow(String jobId) throws SchedulerException;
}
```
ServiceImpl
```
package com.lty.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lty.mapper.QuartzJobMapper;
import com.lty.model.entity.QuartzJob;
import com.lty.service.QuartzJobService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class QuartzJobServiceImpl extends ServiceImpl implements QuartzJobService {
private final Scheduler scheduler;
@Override
public void addJob(QuartzJob job) throws SchedulerException {
// 1. 验证Cron表达式
if (!CronExpression.isValidExpression(job.getCron())) {
throw new IllegalArgumentException("无效的Cron表达式: " + job.getCron());
}
// 2. 创建JobDetail
JobDetail jobDetail = JobBuilder.newJob(getJobClass(job.getJobClass()))
.withIdentity(job.getId(), "JOB_GROUP")
.withDescription(job.getDescription())
.build();
// 3. 设置参数
if (job.getParams() != null) {
jobDetail.getJobDataMap().put("params", job.getParams());
}
// 4. 创建CronTrigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(job.getId() + "_TRIGGER", "TRIGGER_GROUP")
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())
.withMisfireHandlingInstructionDoNothing()) // 错过触发的处理策略
.build();
// 5. 注册任务
scheduler.scheduleJob(jobDetail, trigger);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateJob(QuartzJob job) throws SchedulerException {
updateById(job); // 更新数据库记录
}
@Override
public void deleteJob(String jobId) throws SchedulerException {
// 1. 暂停该任务的触发器(Trigger)
scheduler.pauseTrigger(TriggerKey.triggerKey(jobId));
// 2. 解除该触发器与任务的绑定关系(从调度器中移除 Trigger)
scheduler.unscheduleJob(TriggerKey.triggerKey(jobId));
// 3. 最终删除 Job 本身
scheduler.deleteJob(new JobKey(jobId, "JOB_GROUP"));
// 删除数据库记录
removeById(jobId);
}
@Override
public void pauseJob(String jobId) throws SchedulerException {
// 1. 暂停任务
scheduler.pauseJob(new JobKey(jobId, "JOB_GROUP"));
// 2. 更新数据库状态
QuartzJob job = getById(jobId);
if (Objects.nonNull(job)) {
job.setStatus(1); // 1-暂停
updateById(job);
}
}
@Override
public void resumeJob(String jobId) throws SchedulerException {
// 1. 恢复任务
scheduler.resumeJob(new JobKey(jobId, "JOB_GROUP"));
// 2. 更新数据库状态
QuartzJob job = getById(jobId);
if (Objects.nonNull(job)) {
job.setStatus(0); // 0-正常
updateById(job);
}
}
@Override
public void runJobNow(String jobId) throws SchedulerException {
JobKey jobKey = new JobKey(jobId);
scheduler.triggerJob(jobKey);
log.info("立即执行任务: {}", jobId);
}
// 获取Job类
private Class extends Job> getJobClass(String className) {
try {
return (Class extends Job>) Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("找不到Job类: " + className, e);
}
}
}
```
controller
```
package com.lty.controller;
import cn.hutool.core.util.IdUtil;
import com.lty.model.entity.QuartzJob;
import com.lty.service.QuartzJobService;
import org.springframework.web.bind.annotation.PathVariable;
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;
import java.util.List;
@RestController
@RequestMapping("/job")
public class QuartzJobController {
private final QuartzJobService jobService;
public QuartzJobController(QuartzJobService jobService) {
this.jobService = jobService;
}
/**
* 获取所有任务列表
*/
@PostMapping("/list")
public List listJobs() {
return jobService.list();
}
/**
* 添加任务
*/
@PostMapping("/add")
public String addJob(@RequestBody QuartzJob job) {
try {
job.setId(IdUtil.simpleUUID());
jobService.addJob(job);
// 保存到数据库
jobService.save(job);
return "任务添加成功";
} catch (Exception e) {
return "任务添加失败: " + e.getMessage();
}
}
/**
* 更新任务
*/
@PostMapping("/update")
public String updateJob(@RequestBody QuartzJob job) {
try {
jobService.updateJob(job);
return "任务更新成功";
} catch (Exception e) {
return "任务更新失败: " + e.getMessage();
}
}
/**
* 删除任务
*/
@PostMapping("/{jobId}/delete")
public String deleteJob(@PathVariable String jobId) {
try {
jobService.deleteJob(jobId);
return "任务删除成功";
} catch (Exception e) {
return "任务删除失败: " + e.getMessage();
}
}
/**
* 暂停任务
*/
@PostMapping("/{jobId}/pause")
public String pauseJob(@PathVariable String jobId) {
try {
jobService.pauseJob(jobId);
return "任务暂停成功";
} catch (Exception e) {
return "任务暂停失败: " + e.getMessage();
}
}
/**
* 恢复任务
*/
@PostMapping("/{jobId}/resume")
public String resumeJob(@PathVariable String jobId) {
try {
jobService.resumeJob(jobId);
return "任务恢复成功";
} catch (Exception e) {
return "任务恢复失败: " + e.getMessage();
}
}
/**
* 立即执行任务
*/
@PostMapping("/{jobId}/runNow")
public String runJobNow(@PathVariable String jobId) {
try {
jobService.runJobNow(jobId);
return "任务已立即执行";
} catch (Exception e) {
return "任务立即执行失败: " + e.getMessage();
}
}
/**
* 重启定时任务
*/
@PostMapping("/restart")
public void restart() {
try {
List list = jobService.list();
for (QuartzJob job : list) {
if (job.getStatus() == 0) {
// 0-正常,重新添加任务
jobService.addJob(job);
} else if (job.getStatus() == 1) {
// 1-暂停,暂停任务
jobService.pauseJob(job.getId());
}
}
} catch (Exception e) {
}
}
}
```
-----
### 使用测试
> 专门设置一个job文件夹存放各种定时任务实现
示例定时任务
```
package com.lty.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 示例定时任务
* 该类实现了Quartz的Job接口,用于执行定时任务。
*/
@Slf4j
public class DemoJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取参数
String params = context.getJobDetail().getJobDataMap().getString("params");
log.info("执行定时任务: {}, 参数: {}",
context.getJobDetail().getKey().getName(), params);
// 执行具体业务逻辑...
}
}
```
添加任务测试参数
```
{
"jobClass": "com.lty.job.DemoJob",
"cron": "0/10 * * * * ?",
"params": "testParam",
"description": "每10秒执行一次的测试任务",
"status": 0
}
```
### Tip
需要多个“类似”任务
方案 1:使用不同的类名
方案 2:使用同一类名 + 不同参数(推荐)
方案 3:自定义 Group 分组