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
































