SpringBoot?實現(xiàn)動態(tài)添加定時任務(wù)功能
最近的需求有一個自動發(fā)布的功能, 需要做到每次提交都要動態(tài)的添加一個定時任務(wù)
代碼結(jié)構(gòu)
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; /** * 調(diào)度器配置 * * @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. 定時任務(wù)類型枚舉
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; /** * 任務(wù)類型 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:16 */ @AllArgsConstructor public enum TaskType { /** * 發(fā)布任務(wù) */ RELEASE(id -> new ReleaseTaskImpl((Long) id)) { @Override public String getKey(Object params) { return Const.RELEASE + "-" + params; } }, * 調(diào)度任務(wù) SCHEDULER_TASK(id -> new SchedulerTaskImpl((Long) id)) { return Const.TASK + "-" + params; ; private final Function<Object, Runnable> factory; * 創(chuàng)建任務(wù) * * @param params params * @return task public Runnable create(Object params) { return factory.apply(params); } * 獲取 key * @return key public abstract String getKey(Object params); }
這個枚舉的作用是生成定時任務(wù)的 runnable 和 定時任務(wù)的唯一值, 方便后續(xù)維護
3. 實際執(zhí)行任務(wù)實現(xiàn)類
package com.orion.ops.handler.scheduler.impl; import com.orion.ops.service.api.ApplicationReleaseService; import com.orion.spring.SpringHolder; import lombok.extern.slf4j.Slf4j; /** * 發(fā)布任務(wù)實現(xiàn) * * @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("定時執(zhí)行發(fā)布任務(wù)-觸發(fā) releaseId: {}", releaseId); applicationReleaseService.runnableAppRelease(releaseId, true); }
4. 定時任務(wù)包裝器
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; /** * 定時 任務(wù)包裝器 * * @author Jiahang Li * @version 1.0.0 * @since 2022/2/14 10:34 */ public class TimedTask { /** * 任務(wù) */ private Runnable runnable; * 異步執(zhí)行 private volatile ScheduledFuture<?> future; public TimedTask(Runnable runnable) { this.runnable = runnable; } * 提交任務(wù) 一次性 * * @param scheduler scheduler * @param time time public void submit(TaskScheduler scheduler, Date time) { this.future = scheduler.schedule(runnable, time); * 提交任務(wù) cron表達式 * @param trigger trigger public void submit(TaskScheduler scheduler, Trigger trigger) { this.future = scheduler.schedule(runnable, trigger); * 取消定時任務(wù) public void cancel() { if (future != null) { future.cancel(true); } }
這個類的作用是包裝實際執(zhí)行任務(wù), 以及提供調(diào)度器的執(zhí)行方法
5. 任務(wù)注冊器 (核心)
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; /** * 任務(wù)注冊器 * * @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; /** * 提交任務(wù) * * @param type type * @param time time * @param params params */ public void submit(TaskType type, Date time, Object params) { // 獲取任務(wù) TimedTask timedTask = this.getTask(type, params); // 執(zhí)行任務(wù) timedTask.submit(scheduler, time); } * @param cron cron public void submit(TaskType type, String cron, Object params) { timedTask.submit(scheduler, new CronTrigger(cron)); * 獲取任務(wù) private TimedTask getTask(TaskType type, Object params) { // 生成任務(wù) Runnable runnable = type.create(params); String key = type.getKey(params); // 判斷是否存在任務(wù) if (taskMap.containsKey(key)) { throw Exceptions.init(MessageConst.TASK_PRESENT); } TimedTask timedTask = new TimedTask(runnable); taskMap.put(key, timedTask); return timedTask; * 取消任務(wù) 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(); }
這個類提供了執(zhí)行, 提交任務(wù)的api, 實現(xiàn) DisposableBean 接口, 便于在bean銷毀時將任務(wù)一起銷毀
6. 使用
@Resource private TaskRegister taskRegister; /** * 提交發(fā)布 */ @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); // 提交任務(wù) taskRegister.submit(TaskType.RELEASE, request.getTimedReleaseTime(), id); return id; }
最后
這是一個簡單的動態(tài)添加定時任務(wù)的工具, 有很多的改造空間, 比如想持久化可以插入到庫中, 定義一個 CommandLineRunner 在啟動時將定時任務(wù)全部加載, 還可以給任務(wù)加鉤子自動提交,自動刪除等, 代碼直接cv一定會報錯, 就是一些工具, 常量類會報錯, 改改就好了, 本人已親測可用, 有什么問題可以在評論區(qū)溝通
到此這篇關(guān)于SpringBoot 實現(xiàn)動態(tài)添加定時任務(wù)功能的文章就介紹到這了,更多相關(guān)SpringBoot 定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?對象在?JVM?中的內(nèi)存布局超詳細(xì)解說
這篇文章主要介紹了Java?對象在?JVM?中的內(nèi)存布局超詳細(xì)解說,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09詳解SpringBoot+Mybatis實現(xiàn)動態(tài)數(shù)據(jù)源切換
這篇文章主要介紹了詳解SpringBoot+Mybatis實現(xiàn)動態(tài)數(shù)據(jù)源切換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Java數(shù)組轉(zhuǎn)換為List的四種方式
這篇文章主要介紹了Java開發(fā)技巧數(shù)組轉(zhuǎn)List的四種方式總結(jié),每種方式結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09VsCode搭建Spring Boot項目并進行創(chuàng)建、運行、調(diào)試
這篇文章主要介紹了VsCode搭建Spring Boot項目并進行創(chuàng)建、運行、調(diào)試 ,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05spring Data jpa簡介_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了spring Data jpa簡介的相關(guān)資料,需要的朋友可以參考下2017-09-09基于maven搭建一個ssm的web項目的詳細(xì)圖文教程
這篇文章主要介紹了基于maven搭建一個ssm的web項目的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09