springBoot自定义cron表达式注册定时任务
一、原理
- 1、使用Spring自带的TaskScheduler注册任务
- 2、注册后返回:ScheduledFuture,用于取消定时任务
- 3、注册任务后不会马上取消任务,所以将任务缓存。在需要取消任务的时候调用取消接口取消
- 4、cron表达式可以由前端或者后端生成。实现中会校验cron表达式
| public class TestScheduled { |
| |
| |
| |
| |
| |
| @Resource |
| private TaskScheduler taskScheduler; |
| |
| public void registrarTask() { |
| |
| Runnable taskRunnable = new Runnable() { |
| @Override |
| public void run() { |
| |
| } |
| }; |
| |
| CronTrigger trigger = new CronTrigger("0/5 * * * * ?"); |
| |
| ScheduledFuture<?> future = this.taskScheduler.schedule(taskRunnable, trigger); |
| |
| future.cancel(true); |
| } |
| } |
二、具体实现
1、配置任务调度器
- 作用:设置:核心线程数:可同时执行任务数;设置线程名称前缀
- 可以不配置。不配置就默认使用spring自带的
| package com.cc.ssd.config; |
| |
| import org.springframework.context.annotation.Bean; |
| import org.springframework.context.annotation.Configuration; |
| import org.springframework.scheduling.TaskScheduler; |
| import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
| |
| |
| |
| |
| |
| @Configuration |
| public class CronTaskConfig { |
| |
| |
| |
| |
| @Bean(name = "taskScheduler") |
| public TaskScheduler taskScheduler() { |
| |
| ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); |
| |
| taskScheduler.setPoolSize(4); |
| taskScheduler.setRemoveOnCancelPolicy(true); |
| |
| taskScheduler.setThreadNamePrefix("Cs-ThreadPool-"); |
| return taskScheduler; |
| } |
| |
| } |
2、定时任务注册类
- 作用:缓存、注册定时任务;还可以查询、删除定时任务
| package com.cc.ssd.registrar; |
| |
| import com.cc.ssd.task.CronTaskFuture; |
| import com.cc.ssd.task.CronTaskRunnable; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.BeanUtils; |
| import org.springframework.beans.factory.DisposableBean; |
| import org.springframework.scheduling.TaskScheduler; |
| import org.springframework.scheduling.config.CronTask; |
| import org.springframework.scheduling.support.CronExpression; |
| import org.springframework.stereotype.Component; |
| import org.springframework.util.Assert; |
| |
| import javax.annotation.Resource; |
| import java.time.LocalDateTime; |
| import java.time.format.DateTimeFormatter; |
| import java.util.*; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.stream.Collectors; |
| |
| |
| |
| |
| @Component |
| public class CronTaskRegistrar implements DisposableBean { |
| |
| private static final Logger log = LoggerFactory.getLogger(CronTaskRegistrar.class); |
| |
| |
| |
| |
| |
| |
| private final Map<Runnable, CronTaskFuture> scheduledTasks = new ConcurrentHashMap<>(16); |
| |
| |
| |
| |
| @Resource(name = "taskScheduler") |
| private TaskScheduler taskScheduler; |
| |
| |
| |
| |
| public TaskScheduler getTaskScheduler() { |
| return this.taskScheduler; |
| } |
| |
| |
| |
| |
| |
| |
| public void addCronTask(Runnable taskRunnable, String cronExpression) { |
| |
| boolean validExpression = CronExpression.isValidExpression(cronExpression); |
| if (!validExpression) { |
| throw new RuntimeException("cron表达式验证失败!"); |
| } |
| |
| CronExpression parse = CronExpression.parse(cronExpression); |
| LocalDateTime next = parse.next(LocalDateTime.now()); |
| if (Objects.nonNull(next)) { |
| |
| String format = next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| log.info("定时任务下次执行的时间:{}", format); |
| } |
| |
| |
| CronTask cronTask = new CronTask(taskRunnable, cronExpression); |
| this.addCronTask(cronTask); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| private void addCronTask(CronTask cronTask) { |
| if (Objects.nonNull(cronTask)) { |
| |
| Runnable task = cronTask.getRunnable(); |
| String taskId = null; |
| if (task instanceof CronTaskRunnable) { |
| taskId = ((CronTaskRunnable) task).getTaskId(); |
| } |
| |
| Runnable taskCache = this.getTaskByTaskId(taskId); |
| if (Objects.nonNull(taskCache) && this.scheduledTasks.containsKey(taskCache)) { |
| this.removeCronTaskByTaskId(taskId); |
| } |
| |
| CronTaskFuture scheduledFutureTask = this.scheduleCronTask(cronTask); |
| |
| |
| this.scheduledTasks.put(task, scheduledFutureTask); |
| |
| |
| |
| } |
| } |
| |
| |
| |
| |
| |
| private CronTaskFuture scheduleCronTask(CronTask cronTask) { |
| |
| CronTaskFuture scheduledTask = new CronTaskFuture(); |
| |
| scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()); |
| |
| return scheduledTask; |
| } |
| |
| |
| |
| |
| public List<CronTaskRunnable> getScheduledTasks() { |
| List<CronTaskRunnable> tasks = new ArrayList<>(); |
| |
| Set<Runnable> keySet = scheduledTasks.keySet(); |
| keySet.forEach(key -> { |
| CronTaskRunnable task = new CronTaskRunnable(); |
| if (key instanceof CronTaskRunnable) { |
| CronTaskRunnable taskParent = (CronTaskRunnable) key; |
| BeanUtils.copyProperties(taskParent, task); |
| } |
| tasks.add(task); |
| }); |
| |
| return tasks.stream() |
| .sorted(Comparator.comparing(CronTaskRunnable::getTaskId)) |
| .collect(Collectors.toList()); |
| } |
| |
| |
| |
| |
| public void removeCronTaskByTaskId(String taskId) { |
| |
| Runnable task = this.getTaskByTaskId(taskId); |
| |
| CronTaskFuture cronTaskFuture = this.scheduledTasks.remove(task); |
| if (Objects.nonNull(cronTaskFuture)) { |
| cronTaskFuture.cancel(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| private Runnable getTaskByTaskId(String taskId) { |
| Assert.notNull(taskId, "任务id不能为空!"); |
| Set<Map.Entry<Runnable, CronTaskFuture>> entries = scheduledTasks.entrySet(); |
| |
| Map.Entry<Runnable, CronTaskFuture> rcf = entries.stream().filter(rf -> { |
| Runnable key = rf.getKey(); |
| String taskId1 = null; |
| if (key instanceof CronTaskRunnable) { |
| taskId1 = ((CronTaskRunnable) key).getTaskId(); |
| } |
| return taskId.equals(taskId1); |
| }).findAny().orElse(null); |
| |
| if (Objects.nonNull(rcf)) { |
| return rcf.getKey(); |
| } |
| return null; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| @Override |
| public void destroy() { |
| |
| for (CronTaskFuture task : this.scheduledTasks.values()) { |
| task.cancel(); |
| } |
| |
| this.scheduledTasks.clear(); |
| |
| log.info("取消所有定时任务!"); |
| |
| } |
| |
| } |
| |
3、定时任务的执行结果ScheduledFuture
- 作用:CronTaskFuture类中使用的是ScheduledFuture对象来表示定时任务的执行结果。
| package com.cc.ssd.task; |
| |
| import java.util.Objects; |
| import java.util.concurrent.ScheduledFuture; |
| |
| |
| |
| |
| |
| |
| public final class CronTaskFuture { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public ScheduledFuture<?> future; |
| |
| |
| |
| |
| |
| |
| |
| |
| public void cancel() { |
| try { |
| |
| ScheduledFuture<?> future = this.future; |
| if (Objects.nonNull(future)) { |
| future.cancel(true); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException("销毁定时任务失败!"); |
| } finally { |
| |
| } |
| |
| } |
| |
| } |
| |
4、具体的任务。
- 实现Runable接口
- 任务处理的方式按照自己的需求去实现即可
| package com.cc.ssd.task; |
| |
| import lombok.Data; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.stereotype.Component; |
| |
| |
| |
| |
| |
| @Data |
| public class CronTaskRunnable implements Runnable { |
| |
| private static final Logger log = LoggerFactory.getLogger(CronTaskRunnable.class); |
| |
| |
| |
| |
| private String taskId; |
| |
| |
| |
| private Integer taskType; |
| |
| |
| |
| private String taskName; |
| |
| |
| |
| private Object[] params; |
| |
| public CronTaskRunnable() { |
| } |
| |
| public CronTaskRunnable(String taskId, Integer taskType, String taskName, Object... params) { |
| this.taskId = taskId; |
| this.taskType = taskType; |
| this.taskName = taskName; |
| this.params = params; |
| } |
| |
| |
| |
| |
| |
| @Override |
| public void run() { |
| long start = System.currentTimeMillis(); |
| |
| |
| log.info("\n\t {}号.定时任务开始执行 - taskId:{},taskName:{},taskType:{},params:{}", |
| taskType, taskId, taskName, taskType, params); |
| |
| |
| |
| |
| |
| |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| throw new RuntimeException(e); |
| } |
| |
| log.info("\n\t {}号.任务执行完成 - 耗时:{},taskId:{},taskType:{}", |
| taskType, System.currentTimeMillis() - start, taskId, taskType); |
| |
| } |
| |
| |
| } |
| |
5、测试Controller
| package com.cc.ssd.web.controller; |
| |
| import com.cc.ssd.registrar.CronTaskRegistrar; |
| import com.cc.ssd.task.CronTaskRunnable; |
| import org.springframework.web.bind.annotation.*; |
| |
| import javax.annotation.Resource; |
| import java.util.List; |
| import java.util.Map; |
| |
| |
| |
| |
| |
| @RestController |
| @RequestMapping("/scheduled") |
| public class TestScheduledController { |
| |
| @Resource |
| private CronTaskRegistrar cronTaskRegistrar; |
| |
| |
| |
| |
| |
| |
| @GetMapping |
| public List<CronTaskRunnable> getScheduledTasks() { |
| return cronTaskRegistrar.getScheduledTasks(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| @PostMapping |
| public String addCronTask(@RequestBody Map<String, Object> param) { |
| |
| String taskId = (String) param.get("taskId"); |
| Integer taskType = (Integer) param.get("taskType"); |
| String taskName = (String) param.get("taskName"); |
| Object params = param.get("params"); |
| |
| CronTaskRunnable task = new CronTaskRunnable(taskId, taskType, taskName, params); |
| |
| cronTaskRegistrar.addCronTask(task, "0/5 * * * * ?"); |
| return "ok"; |
| } |
| |
| |
| |
| |
| |
| |
| |
| @DeleteMapping |
| public String removeCronTaskByTaskId(@RequestParam String taskId) { |
| cronTaskRegistrar.removeCronTaskByTaskId(taskId); |
| return "ok"; |
| } |
| |
| |
| |
| |
| |
| |
| @DeleteMapping("/removeAll") |
| public String removeCronTask() { |
| cronTaskRegistrar.destroy(); |
| return "ok"; |
| } |
| |
| } |
| |
6、最后效果

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)