Spring Boot 動態(tài)定時任務(wù):TaskScheduler 極簡教程
引言
在日常開發(fā)中,定時任務(wù)是常見的功能需求,例如數(shù)據(jù)同步、日志清理、報表生成等。傳統(tǒng)的靜態(tài)定時任務(wù)(如基于@Scheduled注解)雖然實現(xiàn)簡單,但無法滿足任務(wù)動態(tài)添加、修改、刪除的場景。本文將詳細(xì)介紹如何基于Spring Boot內(nèi)置的TaskScheduler,實現(xiàn)一套靈活可擴展的動態(tài)定時任務(wù)系統(tǒng),幫助開發(fā)者解決動態(tài)調(diào)度需求。
核心概念與原理
TaskScheduler 簡介
TaskScheduler是Spring框架提供的任務(wù)調(diào)度接口,用于統(tǒng)一管理定時任務(wù)的觸發(fā)與執(zhí)行。它封裝了不同的調(diào)度實現(xiàn)(如JDK的ScheduledThreadPoolExecutor、Quartz等),提供了豐富的調(diào)度方法,支持基于固定速率、固定延遲、Cron表達式等多種調(diào)度策略。
在Spring Boot中,當(dāng)引入spring-context依賴(默認(rèn)已包含在spring-boot-starter中)時,會自動配置ThreadPoolTaskScheduler(TaskScheduler的默認(rèn)實現(xiàn)),無需額外引入第三方調(diào)度框架,降低了開發(fā)成本。
動態(tài)定時任務(wù)核心邏輯
動態(tài)定時任務(wù)的核心在于動態(tài)管理,即支持在應(yīng)用運行過程中,對任務(wù)進行添加、更新、刪除操作。其實現(xiàn)邏輯如下:
- 任務(wù)存儲:使用線程安全的容器(如ConcurrentHashMap)存儲任務(wù)信息,確保多線程環(huán)境下的操作安全性。
 - 任務(wù)調(diào)度:通過TaskScheduler的schedule(Runnable task, Trigger trigger)方法,將任務(wù)與觸發(fā)規(guī)則(如CronTrigger)綁定,返回ScheduledFuture對象用于后續(xù)任務(wù)控制。
 - 任務(wù)更新/刪除:更新任務(wù)時,先通過舊任務(wù)的ScheduledFuture對象取消任務(wù),再重新創(chuàng)建新任務(wù)并調(diào)度;刪除任務(wù)時,直接通過ScheduledFuture取消任務(wù)并從容器中移除。
 
完整實現(xiàn)
任務(wù)實體類(ScheduledTask)
定義任務(wù)實體,存儲任務(wù)的核心信息(任務(wù)ID、Cron表達式、描述、執(zhí)行邏輯、調(diào)度結(jié)果等):
/**
 * 定時任務(wù)實體類
 * 封裝任務(wù)信息、調(diào)度規(guī)則及調(diào)度結(jié)果
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScheduledTask {
    // 任務(wù)唯一標(biāo)識(用于動態(tài)管理)
    private String taskId;
    // 任務(wù)Cron表達式(調(diào)度規(guī)則)
    private String cronExpression;
    // 任務(wù)描述(備注信息)
    private String description;
    // 任務(wù)執(zhí)行邏輯(Runnable接口實現(xiàn))
    private Runnable task;
    // 調(diào)度結(jié)果(用于取消任務(wù))
    private ScheduledFuture<?> scheduledFuture;
    /**
     * 構(gòu)建任務(wù)執(zhí)行邏輯
     * 可根據(jù)實際需求自定義任務(wù)內(nèi)容
     */
    public void buildTask() {
        this.task = () -> {
            // 任務(wù)執(zhí)行內(nèi)容(示例:打印任務(wù)信息)
            System.out.printf("[%s] 動態(tài)定時任務(wù)執(zhí)行 - 任務(wù)ID:%s,描述:%s%n",
                    System.currentTimeMillis(), taskId, description);
            // 實際業(yè)務(wù)邏輯可在此處擴展(如數(shù)據(jù)同步、文件處理等)
        };
    }
    /**
     * 基于Cron表達式創(chuàng)建觸發(fā)規(guī)則
     * @return Trigger 觸發(fā)規(guī)則
     */
    public Trigger buildTrigger() {
        return new CronTrigger(cronExpression);
    }
    /**
     * 調(diào)度任務(wù)(通過TaskScheduler執(zhí)行)
     * @param taskScheduler 任務(wù)調(diào)度器
     */
    public void schedule(TaskScheduler taskScheduler) {
        this.scheduledFuture = taskScheduler.schedule(task, buildTrigger());
    }
    /**
     * 取消任務(wù)(停止調(diào)度)
     * @param mayInterruptIfRunning 是否允許中斷正在執(zhí)行的任務(wù)
     * @return boolean 取消結(jié)果(true:取消成功,false:取消失敗)
     */
    public boolean cancelTask(boolean mayInterruptIfRunning) {
        if (scheduledFuture != null && !scheduledFuture.isCancelled()) {
            return scheduledFuture.cancel(mayInterruptIfRunning);
        }
        returnfalse;
    }
}任務(wù)調(diào)度器配置(TaskSchedulerConfig)
配置ThreadPoolTaskScheduler,自定義線程池參數(shù)(如核心線程數(shù)、線程名稱前綴等),確保任務(wù)調(diào)度的高效性和穩(wěn)定性:
/**
 * 任務(wù)調(diào)度器配置類
 * 自定義ThreadPoolTaskScheduler參數(shù),優(yōu)化調(diào)度性能
 */
@Configuration
public class TaskSchedulerConfig {
    /**
     * 配置TaskScheduler實例
     * @return TaskScheduler 任務(wù)調(diào)度器
     */
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 核心線程數(shù)(根據(jù)任務(wù)數(shù)量調(diào)整,避免線程過多導(dǎo)致資源浪費)
        taskScheduler.setPoolSize(5);
        // 線程名稱前綴(便于日志排查)
        taskScheduler.setThreadNamePrefix("dynamic-task-scheduler-");
        // 任務(wù)拒絕策略(當(dāng)線程池滿時,直接拋出異常)
        taskScheduler.setRejectedExecutionHandler((runnable, executor) -> {
            throw new RuntimeException("任務(wù)調(diào)度線程池已滿,無法執(zhí)行新任務(wù):" + runnable.getClass().getName());
        });
        // 初始化調(diào)度器(必須調(diào)用,否則無法生效)
        taskScheduler.initialize();
        return taskScheduler;
    }
}任務(wù)管理器(TaskManager)
核心服務(wù)類,負(fù)責(zé)任務(wù)的添加、更新、刪除、查詢等動態(tài)管理操作,使用ConcurrentHashMap存儲任務(wù),確保線程安全:
/**
 * 定時任務(wù)管理器
 * 提供任務(wù)的動態(tài)管理(添加、更新、刪除、查詢)
 */
@Service
public class TaskManager {
    // 線程安全的任務(wù)容器(key:taskId,value:ScheduledTask)
    private final Map<String, ScheduledTask> taskMap = new ConcurrentHashMap<>();
    // 注入Spring配置的TaskScheduler
    @Autowired
    private TaskScheduler taskScheduler;
    /**
     * 添加定時任務(wù)
     * @param taskId 任務(wù)ID(唯一)
     * @param cronExpression Cron表達式
     * @param description 任務(wù)描述
     * @return boolean 添加結(jié)果(true:成功,false:任務(wù)已存在)
     */
    public boolean addTask(String taskId, String cronExpression, String description) {
        // 校驗任務(wù)是否已存在
        if (taskMap.containsKey(taskId)) {
            System.out.printf("任務(wù)ID:%s 已存在,添加失敗%n", taskId);
            returnfalse;
        }
        // 構(gòu)建任務(wù)實例
        ScheduledTask task = new ScheduledTask();
        task.setTaskId(taskId);
        task.setCronExpression(cronExpression);
        task.setDescription(description);
        task.buildTask(); // 初始化任務(wù)執(zhí)行邏輯
        // 調(diào)度任務(wù)
        task.schedule(taskScheduler);
        // 存入任務(wù)容器
        taskMap.put(taskId, task);
        System.out.printf("任務(wù)添加成功 - 任務(wù)ID:%s,Cron表達式:%s%n", taskId, cronExpression);
        returntrue;
    }
    /**
     * 更新任務(wù)的Cron表達式(修改調(diào)度規(guī)則)
     * @param taskId 任務(wù)ID
     * @param newCronExpression 新的Cron表達式
     * @return boolean 更新結(jié)果(true:成功,false:任務(wù)不存在)
     */
    public boolean updateTaskCron(String taskId, String newCronExpression) {
        // 校驗任務(wù)是否存在
        ScheduledTask oldTask = taskMap.get(taskId);
        if (oldTask == null) {
            System.out.printf("任務(wù)ID:%s 不存在,更新失敗%n", taskId);
            returnfalse;
        }
        // 1. 取消舊任務(wù)
        boolean cancelResult = oldTask.cancelTask(false);
        if (!cancelResult) {
            System.out.printf("任務(wù)ID:%s 取消失敗,更新中斷%n", taskId);
            returnfalse;
        }
        // 2. 創(chuàng)建新任務(wù)(復(fù)用舊任務(wù)的描述和執(zhí)行邏輯,更新Cron表達式)
        ScheduledTask newTask = new ScheduledTask();
        newTask.setTaskId(taskId);
        newTask.setCronExpression(newCronExpression);
        newTask.setDescription(oldTask.getDescription());
        newTask.buildTask();
        // 3. 調(diào)度新任務(wù)
        newTask.schedule(taskScheduler);
        // 4. 替換任務(wù)容器中的舊任務(wù)
        taskMap.put(taskId, newTask);
        System.out.printf("任務(wù)更新成功 - 任務(wù)ID:%s,新Cron表達式:%s%n", taskId, newCronExpression);
        returntrue;
    }
    /**
     * 刪除定時任務(wù)
     * @param taskId 任務(wù)ID
     * @return boolean 刪除結(jié)果(true:成功,false:任務(wù)不存在)
     */
    public boolean deleteTask(String taskId) {
        // 校驗任務(wù)是否存在
        ScheduledTask task = taskMap.get(taskId);
        if (task == null) {
            System.out.printf("任務(wù)ID:%s 不存在,刪除失敗%n", taskId);
            returnfalse;
        }
        // 1. 取消任務(wù)
        boolean cancelResult = task.cancelTask(false);
        if (!cancelResult) {
            System.out.printf("任務(wù)ID:%s 取消失敗,刪除中斷%n", taskId);
            returnfalse;
        }
        // 2. 從容器中移除任務(wù)
        taskMap.remove(taskId);
        System.out.printf("任務(wù)刪除成功 - 任務(wù)ID:%s%n", taskId);
        returntrue;
    }
    /**
     * 查詢單個任務(wù)
     * @param taskId 任務(wù)ID
     * @return ScheduledTask 任務(wù)實例(null:任務(wù)不存在)
     */
    public ScheduledTask getTask(String taskId) {
        return taskMap.get(taskId);
    }
    /**
     * 查詢所有任務(wù)
     * @return Collection<ScheduledTask> 所有任務(wù)列表
     */
    public Collection<ScheduledTask> getAllTasks() {
        return taskMap.values();
    }
}控制器(TaskController)
提供RESTful接口,方便前端或其他服務(wù)調(diào)用,實現(xiàn)任務(wù)的動態(tài)管理:
/**
 * 定時任務(wù)API控制器
 * 提供RESTful接口,實現(xiàn)任務(wù)的動態(tài)管理
 */
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
    @Autowired
    private TaskManager taskManager;
    /**
     * 添加任務(wù)
     * @param params 請求參數(shù)(taskId、cronExpression、description)
     * @return ResponseEntity<Map<String, Object>> 響應(yīng)結(jié)果
     */
    @PostMapping
    public ResponseEntity<Map<String, Object>> addTask(@RequestBody Map<String, String> params) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 校驗參數(shù)
            String taskId = params.get("taskId");
            String cronExpression = params.get("cronExpression");
            String description = params.getOrDefault("description", "默認(rèn)任務(wù)");
            if (taskId == null || cronExpression == null) {
                result.put("success", false);
                result.put("message", "任務(wù)ID和Cron表達式不能為空");
                return ResponseEntity.badRequest().body(result);
            }
            // 調(diào)用任務(wù)管理器添加任務(wù)
            boolean success = taskManager.addTask(taskId, cronExpression, description);
            result.put("success", success);
            result.put("message", success ? "任務(wù)添加成功" : "任務(wù)已存在");
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "任務(wù)添加失敗:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
    /**
     * 更新任務(wù)Cron表達式
     * @param taskId 任務(wù)ID
     * @param params 請求參數(shù)(newCronExpression)
     * @return ResponseEntity<Map<String, Object>> 響應(yīng)結(jié)果
     */
    @PutMapping("/{taskId}/cron")
    public ResponseEntity<Map<String, Object>> updateTaskCron(
            @PathVariable String taskId,
            @RequestBody Map<String, String> params) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 校驗參數(shù)
            String newCronExpression = params.get("newCronExpression");
            if (newCronExpression == null) {
                result.put("success", false);
                result.put("message", "新的Cron表達式不能為空");
                return ResponseEntity.badRequest().body(result);
            }
            // 調(diào)用任務(wù)管理器更新任務(wù)
            boolean success = taskManager.updateTaskCron(taskId, newCronExpression);
            result.put("success", success);
            result.put("message", success ? "任務(wù)Cron更新成功" : "任務(wù)不存在");
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "任務(wù)更新失?。? + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
    /**
     * 刪除任務(wù)
     * @param taskId 任務(wù)ID
     * @return ResponseEntity<Map<String, Object>> 響應(yīng)結(jié)果
     */
    @DeleteMapping("/{taskId}")
    public ResponseEntity<Map<String, Object>> deleteTask(@PathVariable String taskId) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 調(diào)用任務(wù)管理器刪除任務(wù)
            boolean success = taskManager.deleteTask(taskId);
            result.put("success", success);
            result.put("message", success ? "任務(wù)刪除成功" : "任務(wù)不存在");
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "任務(wù)刪除失?。? + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
    /**
     * 查詢單個任務(wù)
     * @param taskId 任務(wù)ID
     * @return ResponseEntity<Map<String, Object>> 響應(yīng)結(jié)果
     */
    @GetMapping("/{taskId}")
    public ResponseEntity<Map<String, Object>> getTask(@PathVariable String taskId) {
        Map<String, Object> result = new HashMap<>();
        try {
            ScheduledTask task = taskManager.getTask(taskId);
            if (task != null) {
                result.put("success", true);</doubaocanvas>














 
 
 















 
 
 
 