前言
你是否为@Scheduled注解不能动态修改而苦恼?是否在为公司内网框架无法引入外部开源定时任务而焦虑?不需要难过了,你的强来啦!
注:本文旨在通过Java Spring内置的Cron功能实现异步定时任务的处理,适用于大部分中小型项目需求。大型需求需自行寻找更合适的定时任务框架,本文作者未试验大型需求是否支持。
环境准备
- 开发工具与依赖:JDK 8+、Maven、Idea
在Application类上添加@EnableScheduling注解,以启用Spring的定时任务支持:
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
代码编写
1、定时任务封装类
java
import lombok.Data;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.CronTrigger;
import java.util.concurrent.ScheduledFuture;
/**
* 定时任务封装类
* 包含任务的基本信息和执行状态
*
* @author CHENG RONG YU
* @since 2026-04-29
*/
@Data
public class ScheduledTask {
// 任务ID
private Long taskId;
// 任务名称
private String taskName;
// 任务执行逻辑
private Runnable task;
// 任务触发器
private Trigger trigger;
// 任务执行Future对象,用于控制任务
private ScheduledFuture<?> future;
/**
* 构造方法
*
* @param taskId 任务ID
* @param taskName 任务名称
* @param task 任务执行逻辑
* @param cronExpression Cron表达式
*/
public ScheduledTask(Long taskId, String taskName, Runnable task, String cronExpression) {
this.taskId = taskId;
this.taskName = taskName;
this.task = task;
this.trigger = new CronTrigger(cronExpression);
}
}
2、线程池任务调度器配置类
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 线程池任务调度器配置类
* 用于支持动态定时任务的执行
*
* @author CHENG RONG YU
* @since 2026-04-29
*/
@Configuration
public class TaskSchedulerConfig {
/**
* 配置线程池任务调度器
*
* @return ThreadPoolTaskScheduler实例
*/
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
// 设置线程池大小
scheduler.setPoolSize(10);
// 设置线程名称前缀
scheduler.setThreadNamePrefix("dynamic-schedule-");
// 设置关闭时等待任务完成的最大秒数
scheduler.setAwaitTerminationSeconds(60);
// 设置关闭时等待正在执行的任务完成
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
}
3、线程池任务调度器配置类
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* 动态定时任务管理器
* 支持运行时动态添加、修改、删除定时任务
*
* @author CHENG RONG YU
* @since 2026-04-29
*/
@Slf4j
@Configuration
public class DynamicScheduleManager {
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
// 存储所有已注册的定时任务,key为任务ID
private final Map<Long, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>();
/**
* 初始化方法
*/
@PostConstruct
public void init() {
log.info("动态定时任务管理器初始化完成");
log.info("ThreadPoolTaskScheduler 状态: {}", taskScheduler != null ? "已注入" : "未注入");
}
/**
* 添加定时任务
* 如果任务ID已存在,会先移除旧任务再添加新任务
*
* @param taskId 任务ID
* @param taskName 任务名称
* @param task 任务执行逻辑
* @param cronExpression Cron表达式
*/
public void addTask(Long taskId, String taskName, Runnable task, String cronExpression) {
log.info("准备添加定时任务 - 任务ID: {}, 任务名称: {}, Cron: {}", taskId, taskName, cronExpression);
// 验证Cron表达式
try {
CronTrigger trigger = new CronTrigger(cronExpression);
Date nextExecution = trigger.nextExecutionTime(new org.springframework.scheduling.TriggerContext() {
@Override
public Date lastScheduledExecutionTime() {
return new Date();
}
@Override
public Date lastActualExecutionTime() {
return new Date();
}
@Override
public Date lastCompletionTime() {
return new Date();
}
});
log.info("下次执行时间: {}", nextExecution);
} catch (Exception e) {
log.error("Cron表达式无效 - 任务ID: {}, Cron: {}, 错误: {}", taskId, cronExpression, e.getMessage());
return;
}
// 先移除已存在的同ID任务
removeTask(taskId);
try {
// 创建Cron触发器
CronTrigger trigger = new CronTrigger(cronExpression);
// 调度任务并获取Future对象
ScheduledFuture<?> future = taskScheduler.schedule(task, trigger);
if (future == null) {
log.error("任务调度失败,返回null - 任务ID: {}", taskId);
return;
}
// 封装任务信息
ScheduledTask scheduledTask = new ScheduledTask(taskId, taskName, task, cronExpression);
scheduledTask.setFuture(future);
scheduledTask.setTrigger(trigger);
// 存入任务映射表
scheduledTasks.put(taskId, scheduledTask);
log.info("定时任务添加成功 - 任务ID: {}, 任务名称: {}, Cron表达式: {}, 当前任务总数: {}",
taskId, taskName, cronExpression, scheduledTasks.size());
} catch (Exception e) {
log.error("定时任务添加失败 - 任务ID: {}, 错误信息: {}", taskId, e.getMessage(), e);
}
}
/**
* 移除定时任务
* 会取消任务的执行并从映射表中删除
*
* @param taskId 任务ID
*/
public void removeTask(Long taskId) {
ScheduledTask scheduledTask = scheduledTasks.remove(taskId);
if (scheduledTask != null && scheduledTask.getFuture() != null) {
// 取消任务执行(尝试中断正在执行的任务)
boolean cancelled = scheduledTask.getFuture().cancel(true);
log.info("定时任务已移除 - 任务ID: {}, 取消结果: {}, 是否已完成: {}",
taskId, cancelled, scheduledTask.getFuture().isDone());
}
}
/**
* 更新定时任务的Cron表达式
* 先移除旧任务,再用新的Cron表达式重新添加
*
* @param taskId 任务ID
* @param cronExpression 新的Cron表达式
*/
public void updateTask(Long taskId, String cronExpression) {
ScheduledTask scheduledTask = scheduledTasks.get(taskId);
if (scheduledTask != null) {
// 移除旧任务
removeTask(taskId);
// 用新Cron表达式重新添加
addTask(taskId, scheduledTask.getTaskName(), scheduledTask.getTask(), cronExpression);
log.info("定时任务已更新 - 任务ID: {}, 新Cron表达式: {}", taskId, cronExpression);
} else {
log.warn("定时任务不存在,无法更新 - 任务ID: {}", taskId);
}
}
/**
* 取消定时任务执行
* 与removeTask不同,此方法只取消执行,不从映射表中删除
*
* @param taskId 任务ID
*/
public void cancelTask(Long taskId) {
ScheduledTask scheduledTask = scheduledTasks.get(taskId);
if (scheduledTask != null && scheduledTask.getFuture() != null) {
scheduledTask.getFuture().cancel(false);
log.info("定时任务已取消 - 任务ID: {}", taskId);
}
}
/**
* 恢复已取消的定时任务
* 使用原有的Cron表达式重新启动任务
*
* @param taskId 任务ID
*/
public void resumeTask(Long taskId) {
ScheduledTask scheduledTask = scheduledTasks.get(taskId);
if (scheduledTask != null) {
CronTrigger trigger = (CronTrigger) scheduledTask.getTrigger();
ScheduledFuture<?> future = taskScheduler.schedule(scheduledTask.getTask(), trigger);
scheduledTask.setFuture(future);
log.info("定时任务已恢复 - 任务ID: {}", taskId);
}
}
/**
* 获取所有定时任务
*
* @return 所有定时任务的映射表副本
*/
public Map<Long, ScheduledTask> getAllTasks() {
return new ConcurrentHashMap<>(scheduledTasks);
}
/**
* 获取指定ID的定时任务
*
* @param taskId 任务ID
* @return 定时任务对象,不存在则返回null
*/
public ScheduledTask getTask(Long taskId) {
return scheduledTasks.get(taskId);
}
}
4、日常刷新定时任务及调用
java
/**
* 应用启动时初始化定时任务
* 从数据库加载所有未删除且配置了Cron表达式的任务,并注册到定时任务管理器
*/
@PostConstruct
public void initScheduledTasks() {
log.info("开始加载数据导入定时任务...");
// 构建查询条件 从数据库同步定时任务
......
// 查询符合条件的任务列表
List<Rwxx> taskList = baseMapper.selectList(queryWrapper);
// 遍历任务列表,逐个注册到定时任务管理器
for (Rwxx task : taskList) {
try {
Long taskId = task.getId();
String cronExpression = task.getCronBds();
String taskName = task.getSjyMc();
// 创建任务执行逻辑
Runnable taskRunnable = () -> {
try {
xxx(taskId);//定时任务的具体执行逻辑
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
// 注册定时任务
scheduleManager.addTask(taskId, taskName, taskRunnable, cronExpression);
log.info("加载定时任务成功 - ID: {}, 名称: {}, Cron: {}", taskId, taskName, cronExpression);
} catch (Exception e) {
log.error("加载定时任务失败 - ID: {}, 错误: {}", task.getId(), e.getMessage(), e);
}
}
log.info("定时任务加载完成,共加载 {} 个任务", taskList.size());
}