SpringBoot 實現動態添加定時任務功能
最近的需求有一個自動發佈的功能, 需要做到每次提交都要動態的添加一個定時任務
代碼結構
1. 配置類
package com.orion.ops.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * 調度器配置 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 9:51 */ @EnableScheduling @Configuration public class SchedulerConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(4); scheduler.setRemoveOnCancelPolicy(true); scheduler.setThreadNamePrefix("scheduling-task-"); return scheduler; } }
2. 定時任務類型枚舉
package com.orion.ops.handler.scheduler; import com.orion.ops.consts.Const; import com.orion.ops.handler.scheduler.impl.ReleaseTaskImpl; import com.orion.ops.handler.scheduler.impl.SchedulerTaskImpl; import lombok.AllArgsConstructor; import java.util.function.Function; /** * 任務類型 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:16 */ @AllArgsConstructor public enum TaskType { /** * 發佈任務 */ RELEASE(id -> new ReleaseTaskImpl((Long) id)) { @Override public String getKey(Object params) { return Const.RELEASE + "-" + params; } }, * 調度任務 SCHEDULER_TASK(id -> new SchedulerTaskImpl((Long) id)) { return Const.TASK + "-" + params; ; private final Function<Object, Runnable> factory; * 創建任務 * * @param params params * @return task public Runnable create(Object params) { return factory.apply(params); } * 獲取 key * @return key public abstract String getKey(Object params); }
這個枚舉的作用是生成定時任務的 runnable 和 定時任務的唯一值, 方便後續維護
3. 實際執行任務實現類
package com.orion.ops.handler.scheduler.impl; import com.orion.ops.service.api.ApplicationReleaseService; import com.orion.spring.SpringHolder; import lombok.extern.slf4j.Slf4j; /** * 發佈任務實現 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:25 */ @Slf4j public class ReleaseTaskImpl implements Runnable { protected static ApplicationReleaseService applicationReleaseService = SpringHolder.getBean(ApplicationReleaseService.class); private Long releaseId; public ReleaseTaskImpl(Long releaseId) { this.releaseId = releaseId; } @Override public void run() { log.info("定時執行發佈任務-觸發 releaseId: {}", releaseId); applicationReleaseService.runnableAppRelease(releaseId, true); }
4. 定時任務包裝器
package com.orion.ops.handler.scheduler; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.Trigger; import java.util.Date; import java.util.concurrent.ScheduledFuture; /** * 定時 任務包裝器 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:34 */ public class TimedTask { /** * 任務 */ private Runnable runnable; * 異步執行 private volatile ScheduledFuture<?> future; public TimedTask(Runnable runnable) { this.runnable = runnable; } * 提交任務 一次性 * * @param scheduler scheduler * @param time time public void submit(TaskScheduler scheduler, Date time) { this.future = scheduler.schedule(runnable, time); * 提交任務 cron表達式 * @param trigger trigger public void submit(TaskScheduler scheduler, Trigger trigger) { this.future = scheduler.schedule(runnable, trigger); * 取消定時任務 public void cancel() { if (future != null) { future.cancel(true); } }
這個類的作用是包裝實際執行任務, 以及提供調度器的執行方法
5. 任務註冊器 (核心)
package com.orion.ops.handler.scheduler; import com.orion.ops.consts.MessageConst; import com.orion.utils.Exceptions; import com.orion.utils.collect.Maps; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; import java.util.Map; /** * 任務註冊器 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:46 */ @Component public class TaskRegister implements DisposableBean { private final Map<String, TimedTask> taskMap = Maps.newCurrentHashMap(); @Resource @Qualifier("taskScheduler") private TaskScheduler scheduler; /** * 提交任務 * * @param type type * @param time time * @param params params */ public void submit(TaskType type, Date time, Object params) { // 獲取任務 TimedTask timedTask = this.getTask(type, params); // 執行任務 timedTask.submit(scheduler, time); } * @param cron cron public void submit(TaskType type, String cron, Object params) { timedTask.submit(scheduler, new CronTrigger(cron)); * 獲取任務 private TimedTask getTask(TaskType type, Object params) { // 生成任務 Runnable runnable = type.create(params); String key = type.getKey(params); // 判斷是否存在任務 if (taskMap.containsKey(key)) { throw Exceptions.init(MessageConst.TASK_PRESENT); } TimedTask timedTask = new TimedTask(runnable); taskMap.put(key, timedTask); return timedTask; * 取消任務 public void cancel(TaskType type, Object params) { TimedTask task = taskMap.get(key); if (task != null) { taskMap.remove(key); task.cancel(); * 是否存在 public boolean has(TaskType type, Object params) { return taskMap.containsKey(type.getKey(params)); @Override public void destroy() { taskMap.values().forEach(TimedTask::cancel); taskMap.clear(); }
這個類提供瞭執行, 提交任務的api, 實現 DisposableBean 接口, 便於在bean銷毀時將任務一起銷毀
6. 使用
@Resource private TaskRegister taskRegister; /** * 提交發佈 */ @RequestMapping("/submit") @EventLog(EventType.SUBMIT_RELEASE) public Long submitAppRelease(@RequestBody ApplicationReleaseRequest request) { Valid.notBlank(request.getTitle()); Valid.notNull(request.getAppId()); Valid.notNull(request.getProfileId()); Valid.notNull(request.getBuildId()); Valid.notEmpty(request.getMachineIdList()); TimedReleaseType timedReleaseType = Valid.notNull(TimedReleaseType.of(request.getTimedRelease())); if (TimedReleaseType.TIMED.equals(timedReleaseType)) { Date timedReleaseTime = Valid.notNull(request.getTimedReleaseTime()); Valid.isTrue(timedReleaseTime.compareTo(new Date()) > 0, MessageConst.TIMED_GREATER_THAN_NOW); } // 提交 Long id = applicationReleaseService.submitAppRelease(request); // 提交任務 taskRegister.submit(TaskType.RELEASE, request.getTimedReleaseTime(), id); return id; }
最後
這是一個簡單的動態添加定時任務的工具, 有很多的改造空間, 比如想持久化可以插入到庫中, 定義一個 CommandLineRunner 在啟動時將定時任務全部加載, 還可以給任務加鉤子自動提交,自動刪除等, 代碼直接cv一定會報錯, 就是一些工具, 常量類會報錯, 改改就好瞭, 本人已親測可用, 有什麼問題可以在評論區溝通
到此這篇關於SpringBoot 實現動態添加定時任務功能的文章就介紹到這瞭,更多相關SpringBoot 定時任務內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解Spring DeferredResult異步操作使用場景
- springboot定時任務@Scheduled執行多次的問題
- 關於@Scheduled註解的任務為什麼不執行的問題
- Java guava monitor監視器線程的使用詳解
- springBoot @Scheduled實現多個任務同時開始執行