springboot使用Scheduling實現(xiàn)動態(tài)增刪啟停定時任務(wù)教程
在項目開發(fā)過程中,如果是一些簡單的工程,非分布式工程,一般我們可以使用@EnableScheduling注解和@Scheduled注解實現(xiàn)簡單的定時任務(wù),也可以使用SchedulingConfigurer接口來實現(xiàn)定時任務(wù)。那如何動態(tài)的來生成定時任務(wù)呢?
下面是具體步驟,可以結(jié)合數(shù)據(jù)庫,來存儲定時任務(wù)所需要的參數(shù)數(shù)據(jù),如bean的名稱、方法名,方法參數(shù)、執(zhí)行的表達式等等。
1、配置定時任務(wù)需要的線程池
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; // 配置定時任務(wù)線程池 @Configuration public class SchedulingConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // 定時任務(wù)執(zhí)行線程池核心線程數(shù) taskScheduler.setPoolSize(4); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-test-"); return taskScheduler; } }
2、創(chuàng)建ScheduledFuture的包裝類
// ScheduledFuture的包裝類 public final class ScheduledTask { volatile ScheduledFuture<?> future; /** * 取消定時任務(wù) */ public void cancel() { ScheduledFuture<?> future = this.future; if (future != null) { future.cancel(true); } } }
3、注冊定時任務(wù),增加、刪除任務(wù)
/** * 添加定時任務(wù)注冊,用來增加、刪除定時任務(wù)。 */ @Component public class CronTaskRegistrar implements DisposableBean { private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16); @Autowired private TaskScheduler taskScheduler; public TaskScheduler getScheduler() { return this.taskScheduler; } // 添加定時任務(wù) public void addCronTask(Runnable task, String cronExpression) { addCronTask(new CronTask(task, cronExpression)); } // 添加定時任務(wù) public void addCronTask(CronTask cronTask) { if (cronTask != null) { Runnable task = cronTask.getRunnable(); if (this.scheduledTasks.containsKey(task)) { removeCronTask(task); } this.scheduledTasks.put(task, scheduleCronTask(cronTask)); } } // 移除定時任務(wù) public void removeCronTask(Runnable task) { ScheduledTask scheduledTask = this.scheduledTasks.remove(task); if (scheduledTask != null) scheduledTask.cancel(); } public ScheduledTask scheduleCronTask(CronTask cronTask) { ScheduledTask scheduledTask = new ScheduledTask(); scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()); return scheduledTask; } @Override public void destroy() { for (ScheduledTask task : this.scheduledTasks.values()) { task.cancel(); } this.scheduledTasks.clear(); } }
4、創(chuàng)建具體執(zhí)行bean中方法的類
// 添加Runnable接口實現(xiàn)類,被定時任務(wù)線程池調(diào)用,用來執(zhí)行指定bean里面的方法。 @Slf4j public class SchedulingRunnable implements Runnable { // bean名稱 private String beanName; // 方法名稱 private String methodName; // 方法參數(shù) private String params; public SchedulingRunnable(String beanName, String methodName) { this(beanName, methodName, null); } public SchedulingRunnable(String beanName, String methodName, String params) { this.beanName = beanName; this.methodName = methodName; this.params = params; } @Override public void run() { log.info("定時任務(wù)開始執(zhí)行 - bean:{},方法:{},參數(shù):{}", beanName, methodName, params); long startTime = System.currentTimeMillis(); try { Object target = SpringContextUtils.getBean(beanName); Method method = null; if (StringUtils.isNotEmpty(params)) { method = target.getClass().getDeclaredMethod(methodName, String.class); } else { method = target.getClass().getDeclaredMethod(methodName); } ReflectionUtils.makeAccessible(method); if (StringUtils.isNotEmpty(params)) { method.invoke(target, params); } else { method.invoke(target); } } catch (Exception ex) { log.error(String.format("定時任務(wù)執(zhí)行異常 - bean:%s,方法:%s,參數(shù):%s ", beanName, methodName, params), ex); } long times = System.currentTimeMillis() - startTime; log.info("定時任務(wù)執(zhí)行結(jié)束 - bean:{},方法:{},參數(shù):{},耗時:{} 毫秒", beanName, methodName, params, times); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SchedulingRunnable that = (SchedulingRunnable) o; if (params == null) { return beanName.equals(that.beanName) && methodName.equals(that.methodName) && that.params == null; } return beanName.equals(that.beanName) && methodName.equals(that.methodName) && params.equals(that.params); } @Override public int hashCode() { if (params == null) { return Objects.hash(beanName, methodName); } return Objects.hash(beanName, methodName, params); } }
5、從spring容器里獲取bean
// 從spring容器里獲取bean @Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtils.applicationContext = applicationContext; } public static Object getBean(String name) { return applicationContext.getBean(name); } public static <T> T getBean(Class<T> requiredType) { return applicationContext.getBean(requiredType); } public static <T> T getBean(String name, Class<T> requiredType) { return applicationContext.getBean(name, requiredType); } public static boolean containsBean(String name) { return applicationContext.containsBean(name); } public static boolean isSingleton(String name) { return applicationContext.isSingleton(name); } public static Class<? extends Object> getType(String name) { return applicationContext.getType(name); } }
6、創(chuàng)建具體執(zhí)行的bean以及方法
// 測試類 @Component("testTask") public class TestTask { /** * 有參方法 * @param params */ public void taskWithParams(String params) { System.out.println("執(zhí)行有參示例任務(wù):" + params); } /** * 無慘方法 */ public void taskNoParams() { System.out.println("執(zhí)行無參示例任務(wù)"); } }
7、接口測試類
@RestController @RequestMapping("/test") public class TestController { @Autowired private CronTaskRegistrar cronTaskRegistrar; @RequestMapping("/addTest") public String addTest(boolean flg){ SchedulingRunnable task; if (flg) { // 創(chuàng)建無慘任務(wù) task = new SchedulingRunnable("testTask", "taskNoParams"); } else { task = new SchedulingRunnable("testTask", "taskWithParams", "hello word"); } cronTaskRegistrar.addCronTask(task, "0/1 * * * * *"); return "ok"; } @RequestMapping("/updateTest") public String updateTest(boolean flg){ //先移除再添加 if(flg) { // 創(chuàng)建無慘任務(wù) SchedulingRunnable task = new SchedulingRunnable("testTask", "taskNoParams"); cronTaskRegistrar.removeCronTask(task); } else { SchedulingRunnable task = new SchedulingRunnable("testTask", "taskWithParams", "hello word"); cronTaskRegistrar.removeCronTask(task); } SchedulingRunnable task = new SchedulingRunnable("testTask", "taskWithParams", "hello word"); cronTaskRegistrar.addCronTask(task, "0/5 * * * * *"); return "ok"; } @RequestMapping("/delTest") public String delTest(){ SchedulingRunnable task = new SchedulingRunnable("testTask", "taskNoParams"); cronTaskRegistrar.removeCronTask(task); return "ok"; } @RequestMapping("/startTest") public String startTest(boolean flg){ /** * 啟停定時任務(wù)的邏輯就是創(chuàng)建新的任務(wù)或者刪除任務(wù),參數(shù)一致即可 * 可以結(jié)合數(shù)據(jù)庫,將配置信息存入數(shù)據(jù)庫 */ if(flg) { SchedulingRunnable task = new SchedulingRunnable("testTask", "taskNoParams"); cronTaskRegistrar.addCronTask(task, "0/5 * * * * *"); } else { SchedulingRunnable task = new SchedulingRunnable("testTask", "taskNoParams"); cronTaskRegistrar.removeCronTask(task); } return "ok"; } }
8、結(jié)合數(shù)據(jù)庫,創(chuàng)建對應(yīng)的實體
// 定時任務(wù)實體 @Data public class JobEntity { /** * 任務(wù)ID */ private Integer jobId; /** * bean名稱 */ private String beanName; /** * 方法名稱 */ private String methodName; /** * 方法參數(shù) */ private String methodParams; /** * cron表達式 */ private String cronExpression; /** * 狀態(tài)(1正常 0暫停) */ private Integer jobStatus; /** * 備注 */ private String remark; /** * 創(chuàng)建時間 */ private Date createTime; /** * 更新時間 */ private Date updateTime; }
9、讀取數(shù)據(jù)庫要執(zhí)行的任務(wù)
在程序啟動的時候,讀取數(shù)據(jù)庫,并創(chuàng)建要執(zhí)行任務(wù)
/** * @description: 初始化數(shù)據(jù)庫任務(wù),可以再程啟動的時候加載數(shù)據(jù)庫里的任務(wù) * @author: HK * @since: 2025/4/21 18:21 */ @Component @Slf4j public class InitTask implements CommandLineRunner { @Autowired private CronTaskRegistrar cronTaskRegistrar; @Override public void run(String... args) { // 初始加載數(shù)據(jù)庫里狀態(tài)為正常的定時任務(wù) /* List<SysJobPO> jobList = service.getJobList("1"); if (CollectionUtils.isNotEmpty(jobList)) { for (SysJobPO job : jobList) { SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams()); cronTaskRegistrar.addCronTask(task, job.getCronExpression()); } log.info("定時任務(wù)已加載完畢..."); }*/ } }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- springboot定時任務(wù)SchedulingConfigurer異步多線程實現(xiàn)方式
- SpringBoot注解@EnableScheduling定時任務(wù)詳細解析
- SpringBoot使用Scheduling實現(xiàn)定時任務(wù)的示例代碼
- springboot通過SchedulingConfigurer實現(xiàn)多定時任務(wù)注冊及動態(tài)修改執(zhí)行周期(示例詳解)
- springboot項目使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)的案例代碼
- SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)多機器部署問題(推薦)
- SpringBoot Scheduling定時任務(wù)的示例代碼
相關(guān)文章
詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用
這篇文章主要介紹了詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09Java數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)之雙向鏈表
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數(shù)據(jù)結(jié)點中都有兩個指針,分別指向直接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個結(jié)點開始,都可以很方便地訪問它的前驅(qū)結(jié)點和后繼結(jié)點。本文將為大家詳細介紹雙向鏈表的特點與使用,需要的可以參考一下2021-12-12SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方式小結(jié)
這篇文章主要介紹了使用SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Java實現(xiàn)上傳和下載功能(支持多個文件同時上傳)
這篇文章主要介紹了Java實現(xiàn)上傳和下載功能,支持多個文件同時上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-12-12記一次集成swagger2(Knife4j)在線文檔提示:Knude4j文檔請求異常的解決辦法
Knife4j是一個集Swagger2 和 OpenAPI3為一體的增強解決方案,下面這篇文章主要給大家介紹了關(guān)于一次集成swagger2(Knife4j)在線文檔提示:Knude4j文檔請求異常的解決辦法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-02-02SpringBoot集成Aviator實現(xiàn)參數(shù)校驗的代碼工程
Aviator是一個高性能、輕量級的java語言實現(xiàn)的表達式求值引擎,主要用于各種表達式的動態(tài)求值,本文給大家詳細介紹了SpringBoot集成Aviator實現(xiàn)參數(shù)校驗的方法,并通過代碼示例講解的非常詳細,需要的朋友可以參考下2024-11-11