第09章 - 定时任务模块
第09章 - 定时任务模块
9.1 定时任务概述
9.1.1 功能介绍
RuoYi-Cloud的定时任务模块(ruoyi-job)基于Quartz框架实现,提供了在线管理定时任务的功能:
- 在线添加、修改、删除定时任务
- 支持立即执行任务
- 任务执行日志记录
- 支持Cron表达式
- 支持简单触发器配置
- 任务暂停和恢复
9.1.2 项目结构
ruoyi-modules/ruoyi-job/
├── src/main/java/com/ruoyi/job/
│ ├── RuoYiJobApplication.java # 启动类
│ ├── config/
│ │ └── ScheduleConfig.java # Quartz配置
│ ├── controller/
│ │ ├── SysJobController.java # 任务管理控制器
│ │ └── SysJobLogController.java # 任务日志控制器
│ ├── domain/
│ │ ├── SysJob.java # 定时任务实体
│ │ └── SysJobLog.java # 任务日志实体
│ ├── mapper/
│ │ ├── SysJobMapper.java
│ │ └── SysJobLogMapper.java
│ ├── service/
│ │ ├── ISysJobService.java
│ │ ├── ISysJobLogService.java
│ │ └── impl/
│ ├── task/
│ │ └── RyTask.java # 任务示例
│ └── util/
│ ├── AbstractQuartzJob.java # 抽象任务类
│ ├── CronUtils.java # Cron工具类
│ ├── JobInvokeUtil.java # 任务执行工具
│ ├── QuartzDisallowConcurrentExecution.java
│ ├── QuartzJobExecution.java
│ └── ScheduleUtils.java # 调度工具类
└── src/main/resources/
├── bootstrap.yml
└── mapper/job/
9.2 Quartz配置
9.2.1 依赖配置
<!-- quartz依赖 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency>
9.2.2 Quartz配置类
@Configuration
public class ScheduleConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
// quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
// 线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
// JobStore配置
prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
// 集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10");
prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
// sqlserver 启用
// prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
factory.setQuartzProperties(prop);
factory.setSchedulerName("RuoyiScheduler");
// 延时启动
factory.setStartupDelay(1);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
// 可选,QuartzScheduler 启动时更新己存在的Job
factory.setOverwriteExistingJobs(true);
// 设置自动启动,默认为true
factory.setAutoStartup(true);
return factory;
}
}
9.2.3 数据库表结构
Quartz需要以下数据库表:
-- 任务详细信息表
CREATE TABLE QRTZ_JOB_DETAILS (
sched_name varchar(120) NOT NULL COMMENT '调度名称',
job_name varchar(200) NOT NULL COMMENT '任务名称',
job_group varchar(200) NOT NULL COMMENT '任务组名',
description varchar(250) DEFAULT NULL COMMENT '相关介绍',
job_class_name varchar(250) NOT NULL COMMENT '执行任务类名称',
is_durable varchar(1) NOT NULL COMMENT '是否持久化',
is_nonconcurrent varchar(1) NOT NULL COMMENT '是否并发',
is_update_data varchar(1) NOT NULL COMMENT '是否更新数据',
requests_recovery varchar(1) NOT NULL COMMENT '是否接受恢复执行',
job_data blob DEFAULT NULL COMMENT '存放持久化job对象',
PRIMARY KEY (sched_name,job_name,job_group)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 触发器详细信息表
CREATE TABLE QRTZ_TRIGGERS (
sched_name varchar(120) NOT NULL COMMENT '调度名称',
trigger_name varchar(200) NOT NULL COMMENT '触发器名称',
trigger_group varchar(200) NOT NULL COMMENT '触发器组名',
job_name varchar(200) NOT NULL COMMENT '任务名称',
job_group varchar(200) NOT NULL COMMENT '任务组名',
description varchar(250) DEFAULT NULL COMMENT '相关介绍',
next_fire_time bigint(13) DEFAULT NULL COMMENT '上一次触发时间',
prev_fire_time bigint(13) DEFAULT NULL COMMENT '下一次触发时间',
priority int(11) DEFAULT NULL COMMENT '优先级',
trigger_state varchar(16) NOT NULL COMMENT '触发器状态',
trigger_type varchar(8) NOT NULL COMMENT '触发器的类型',
start_time bigint(13) NOT NULL COMMENT '开始时间',
end_time bigint(13) DEFAULT NULL COMMENT '结束时间',
calendar_name varchar(200) DEFAULT NULL COMMENT '日程表名称',
misfire_instr smallint(2) DEFAULT NULL COMMENT '补偿执行的策略',
job_data blob DEFAULT NULL COMMENT '存放持久化job对象',
PRIMARY KEY (sched_name,trigger_name,trigger_group),
KEY sched_name (sched_name,job_name,job_group),
CONSTRAINT QRTZ_TRIGGERS_ibfk_1 FOREIGN KEY (sched_name,job_name,job_group) REFERENCES QRTZ_JOB_DETAILS (sched_name,job_name,job_group)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 更多Quartz表结构请参考sql/quartz.sql
9.3 定时任务实体
9.3.1 任务实体类
public class SysJob extends BaseEntity {
/** 任务ID */
@Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
private Long jobId;
/** 任务名称 */
@Excel(name = "任务名称")
@NotBlank(message = "任务名称不能为空")
@Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
private String jobName;
/** 任务组名 */
@Excel(name = "任务组名")
private String jobGroup;
/** 调用目标字符串 */
@Excel(name = "调用目标字符串")
@NotBlank(message = "调用目标字符串不能为空")
@Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
private String invokeTarget;
/** cron执行表达式 */
@Excel(name = "执行表达式")
@NotBlank(message = "Cron执行表达式不能为空")
@Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
private String cronExpression;
/** cron计划策略 0=默认 1=立即触发执行 2=触发一次执行 3=不触发立即执行 */
@Excel(name = "计划策略", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
/** 是否并发执行(0允许 1禁止) */
@Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
private String concurrent;
/** 任务状态(0正常 1暂停) */
@Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
private String status;
// getter/setter...
}
9.3.2 任务日志实体
public class SysJobLog extends BaseEntity {
/** ID */
@Excel(name = "日志序号")
private Long jobLogId;
/** 任务名称 */
@Excel(name = "任务名称")
private String jobName;
/** 任务组名 */
@Excel(name = "任务组名")
private String jobGroup;
/** 调用目标字符串 */
@Excel(name = "调用目标字符串")
private String invokeTarget;
/** 日志信息 */
@Excel(name = "日志信息")
private String jobMessage;
/** 执行状态(0正常 1失败) */
@Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
private String status;
/** 异常信息 */
@Excel(name = "异常信息")
private String exceptionInfo;
/** 开始时间 */
private Date startTime;
/** 停止时间 */
private Date stopTime;
// getter/setter...
}
9.4 任务调度工具
9.4.1 调度工具类
public class ScheduleUtils {
/**
* 得到quartz任务类
*/
private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
boolean isConcurrent = "0".equals(sysJob.getConcurrent());
return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
}
/**
* 构建任务触发对象
*/
public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
}
/**
* 构建任务键对象
*/
public static JobKey getJobKey(Long jobId, String jobGroup) {
return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, SysJob job) throws Exception {
Class<? extends Job> jobClass = getQuartzJobClass(job);
// 构建job信息
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(getJobKey(jobId, jobGroup))
.build();
// 表达式调度构建器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(jobId, jobGroup))
.withSchedule(cronScheduleBuilder)
.build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
// 判断是否存在
if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(getJobKey(jobId, jobGroup));
}
// 判断任务是否过期
if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) {
// 执行调度任务
scheduler.scheduleJob(jobDetail, trigger);
}
// 暂停任务
if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
scheduler.pauseJob(getJobKey(jobId, jobGroup));
}
}
/**
* 更新定时任务
*/
public static void updateScheduleJob(Scheduler scheduler, SysJob job, String jobGroup) throws Exception {
JobKey jobKey = getJobKey(job.getJobId(), jobGroup);
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
createScheduleJob(scheduler, job);
}
/**
* 立即执行任务
*/
public static void run(Scheduler scheduler, SysJob job) throws Exception {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
JobKey jobKey = getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
scheduler.triggerJob(jobKey, dataMap);
} else {
throw new TaskException("任务不存在或已删除", Code.NOT_FOUND);
}
}
/**
* 暂停任务
*/
public static void pauseJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
scheduler.pauseJob(getJobKey(jobId, jobGroup));
}
/**
* 恢复任务
*/
public static void resumeJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
scheduler.resumeJob(getJobKey(jobId, jobGroup));
}
/**
* 删除定时任务
*/
public static void deleteScheduleJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
scheduler.deleteJob(getJobKey(jobId, jobGroup));
}
/**
* 设置定时任务策略
*/
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) {
switch (job.getMisfirePolicy()) {
case ScheduleConstants.MISFIRE_DEFAULT:
return cb;
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
return cb.withMisfireHandlingInstructionIgnoreMisfires();
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
return cb.withMisfireHandlingInstructionFireAndProceed();
case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing();
default:
throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
+ "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
}
}
}
9.4.2 任务执行工具
public class JobInvokeUtil {
/**
* 执行方法
*/
public static void invokeMethod(SysJob sysJob) throws Exception {
String invokeTarget = sysJob.getInvokeTarget();
String beanName = getBeanName(invokeTarget);
String methodName = getMethodName(invokeTarget);
List<Object[]> methodParams = getMethodParams(invokeTarget);
if (!isValidClassName(beanName)) {
Object bean = SpringUtils.getBean(beanName);
invokeMethod(bean, methodName, methodParams);
} else {
Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();
invokeMethod(bean, methodName, methodParams);
}
}
/**
* 调用任务方法
*/
private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
throws Exception {
if (methodParams != null && methodParams.size() > 0) {
Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
method.invoke(bean, getMethodParamsValue(methodParams));
} else {
Method method = bean.getClass().getDeclaredMethod(methodName);
method.invoke(bean);
}
}
/**
* 校验是否为白名单类
*/
public static boolean isValidClassName(String invokeTarget) {
return StringUtils.startsWithAny(invokeTarget, Constants.JOB_WHITELIST_STR);
}
/**
* 获取bean名称
*/
public static String getBeanName(String invokeTarget) {
String beanName = StringUtils.substringBefore(invokeTarget, "(");
return StringUtils.substringBeforeLast(beanName, ".");
}
/**
* 获取bean方法
*/
public static String getMethodName(String invokeTarget) {
String methodName = StringUtils.substringBefore(invokeTarget, "(");
return StringUtils.substringAfterLast(methodName, ".");
}
/**
* 获取method方法参数相关列表
*/
public static List<Object[]> getMethodParams(String invokeTarget) {
String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
if (StringUtils.isEmpty(methodStr)) {
return null;
}
String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
List<Object[]> classs = new LinkedList<>();
for (int i = 0; i < methodParams.length; i++) {
String str = StringUtils.trimToEmpty(methodParams[i]);
// 字符串类型
if (StringUtils.startsWithAny(str, "'", "\"")) {
classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });
}
// 布尔类型
else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) {
classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
}
// 长整型
else if (StringUtils.endsWith(str, "L")) {
classs.add(new Object[] { Long.valueOf(StringUtils.substringBefore(str, "L")), Long.class });
}
// 浮点型
else if (StringUtils.endsWith(str, "D")) {
classs.add(new Object[] { Double.valueOf(StringUtils.substringBefore(str, "D")), Double.class });
}
// 整型
else {
classs.add(new Object[] { Integer.valueOf(str), Integer.class });
}
}
return classs;
}
}
9.5 任务执行类
9.5.1 抽象任务类
public abstract class AbstractQuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
/**
* 线程本地变量
*/
private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SysJob sysJob = new SysJob();
BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
try {
before(context, sysJob);
if (sysJob != null) {
doExecute(context, sysJob);
}
after(context, sysJob, null);
} catch (Exception e) {
log.error("任务执行异常 - :", e);
after(context, sysJob, e);
}
}
/**
* 执行前
*/
protected void before(JobExecutionContext context, SysJob sysJob) {
threadLocal.set(new Date());
}
/**
* 执行后
*/
protected void after(JobExecutionContext context, SysJob sysJob, Exception e) {
Date startTime = threadLocal.get();
threadLocal.remove();
final SysJobLog sysJobLog = new SysJobLog();
sysJobLog.setJobName(sysJob.getJobName());
sysJobLog.setJobGroup(sysJob.getJobGroup());
sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
sysJobLog.setStartTime(startTime);
sysJobLog.setStopTime(new Date());
long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
if (e != null) {
sysJobLog.setStatus(Constants.FAIL);
String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
sysJobLog.setExceptionInfo(errorMsg);
} else {
sysJobLog.setStatus(Constants.SUCCESS);
}
// 写入数据库当中
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
}
/**
* 执行方法,由子类重载
*/
protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
9.5.2 允许并发执行
public class QuartzJobExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
JobInvokeUtil.invokeMethod(sysJob);
}
}
9.5.3 禁止并发执行
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
JobInvokeUtil.invokeMethod(sysJob);
}
}
9.6 自定义任务
9.6.1 任务示例
@Component("ryTask")
public class RyTask {
/**
* 无参方法
*/
public void ryNoParams() {
System.out.println("执行无参方法");
}
/**
* 有参方法
*/
public void ryParams(String s) {
System.out.println("执行有参方法:" + s);
}
/**
* 多参方法
*/
public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
}
}
9.6.2 调用目标字符串格式
| 示例 | 说明 |
|---|---|
| ryTask.ryNoParams | 调用无参方法 |
| ryTask.ryParams('ry') | 调用字符串参数方法 |
| ryTask.ryMultipleParams('ry', true, 2000L, 316.50D, 100) | 调用多参方法 |
9.6.3 Cron表达式说明
Cron表达式由6或7个字段组成,用空格分隔:
秒 分 时 日 月 周 [年]
| 字段 | 允许值 | 特殊字符 |
|---|---|---|
| 秒 | 0-59 | , - * / |
| 分 | 0-59 | , - * / |
| 时 | 0-23 | , - * / |
| 日 | 1-31 | , - * / ? L W |
| 月 | 1-12或JAN-DEC | , - * / |
| 周 | 1-7或SUN-SAT | , - * / ? L # |
| 年 | 可选,1970-2099 | , - * / |
常用表达式:
| 表达式 | 说明 |
|---|---|
| 0 0 12 * * ? | 每天12点执行 |
| 0 15 10 ? * * | 每天10:15执行 |
| 0 0/5 14 * * ? | 每天14点到14:55,每5分钟执行 |
| 0 0 10,14,16 * * ? | 每天10点、14点、16点执行 |
| 0 0 12 ? * WED | 每周三12点执行 |
| 0 0 12 1 * ? | 每月1日12点执行 |
9.7 任务服务实现
9.7.1 任务服务
@Service
public class SysJobServiceImpl implements ISysJobService {
@Autowired
private Scheduler scheduler;
@Autowired
private SysJobMapper jobMapper;
/**
* 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理
*/
@PostConstruct
public void init() throws Exception {
scheduler.clear();
List<SysJob> jobList = jobMapper.selectJobAll();
for (SysJob job : jobList) {
ScheduleUtils.createScheduleJob(scheduler, job);
}
}
/**
* 新增任务
*/
@Override
@Transactional
public int insertJob(SysJob job) throws Exception {
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.insertJob(job);
if (rows > 0) {
ScheduleUtils.createScheduleJob(scheduler, job);
}
return rows;
}
/**
* 更新任务
*/
@Override
@Transactional
public int updateJob(SysJob job) throws Exception {
SysJob properties = selectJobById(job.getJobId());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
updateSchedulerJob(job, properties.getJobGroup());
}
return rows;
}
/**
* 更新任务的时间表达式
*/
public void updateSchedulerJob(SysJob job, String jobGroup) throws Exception {
Long jobId = job.getJobId();
// 判断是否存在
JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
ScheduleUtils.createScheduleJob(scheduler, job);
}
/**
* 暂停任务
*/
@Override
@Transactional
public int pauseJob(SysJob job) throws Exception {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
}
return rows;
}
/**
* 恢复任务
*/
@Override
@Transactional
public int resumeJob(SysJob job) throws Exception {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
int rows = jobMapper.updateJob(job);
if (rows > 0) {
scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
}
return rows;
}
/**
* 立即运行任务
*/
@Override
@Transactional
public boolean run(SysJob job) throws Exception {
boolean result = false;
Long jobId = job.getJobId();
SysJob tmpObj = selectJobById(jobId);
if (tmpObj != null) {
ScheduleUtils.run(scheduler, tmpObj);
result = true;
}
return result;
}
/**
* 删除任务
*/
@Override
@Transactional
public int deleteJob(SysJob job) throws Exception {
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
int rows = jobMapper.deleteJobById(jobId);
if (rows > 0) {
scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
}
return rows;
}
}
9.8 小结
本章详细介绍了RuoYi-Cloud的定时任务模块,包括:
- 模块概述:基于Quartz的在线任务管理
- Quartz配置:调度器、触发器配置
- 任务实体:任务和日志实体类
- 调度工具:任务创建、更新、删除、执行
- 任务执行:并发与非并发执行
- 自定义任务:任务编写方法
- Cron表达式:表达式语法和常用示例
掌握定时任务模块可以轻松实现系统中的各种定时业务需求。

浙公网安备 33010602011771号