springboot使用Scheduling實現(xiàn)動態(tài)增刪啟停定時任務教程
在項目開發(fā)過程中,如果是一些簡單的工程,非分布式工程,一般我們可以使用@EnableScheduling注解和@Scheduled注解實現(xiàn)簡單的定時任務,也可以使用SchedulingConfigurer接口來實現(xiàn)定時任務。那如何動態(tài)的來生成定時任務呢?
下面是具體步驟,可以結合數據庫,來存儲定時任務所需要的參數數據,如bean的名稱、方法名,方法參數、執(zhí)行的表達式等等。
1、配置定時任務需要的線程池
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; // 配置定時任務線程池 @Configuration public class SchedulingConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // 定時任務執(zhí)行線程池核心線程數 taskScheduler.setPoolSize(4); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-test-"); return taskScheduler; } }
2、創(chuàng)建ScheduledFuture的包裝類
// ScheduledFuture的包裝類 public final class ScheduledTask { volatile ScheduledFuture<?> future; /** * 取消定時任務 */ public void cancel() { ScheduledFuture<?> future = this.future; if (future != null) { future.cancel(true); } } }
3、注冊定時任務,增加、刪除任務
/** * 添加定時任務注冊,用來增加、刪除定時任務。 */ @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; } // 添加定時任務 public void addCronTask(Runnable task, String cronExpression) { addCronTask(new CronTask(task, cronExpression)); } // 添加定時任務 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)); } } // 移除定時任務 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)類,被定時任務線程池調用,用來執(zhí)行指定bean里面的方法。 @Slf4j public class SchedulingRunnable implements Runnable { // bean名稱 private String beanName; // 方法名稱 private String methodName; // 方法參數 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("定時任務開始執(zhí)行 - bean:{},方法:{},參數:{}", 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("定時任務執(zhí)行異常 - bean:%s,方法:%s,參數:%s ", beanName, methodName, params), ex); } long times = System.currentTimeMillis() - startTime; log.info("定時任務執(zhí)行結束 - bean:{},方法:{},參數:{},耗時:{} 毫秒", 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í)行有參示例任務:" + params); } /** * 無慘方法 */ public void taskNoParams() { System.out.println("執(zhí)行無參示例任務"); } }
7、接口測試類
@RestController @RequestMapping("/test") public class TestController { @Autowired private CronTaskRegistrar cronTaskRegistrar; @RequestMapping("/addTest") public String addTest(boolean flg){ SchedulingRunnable task; if (flg) { // 創(chuàng)建無慘任務 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)建無慘任務 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){ /** * 啟停定時任務的邏輯就是創(chuàng)建新的任務或者刪除任務,參數一致即可 * 可以結合數據庫,將配置信息存入數據庫 */ 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、結合數據庫,創(chuàng)建對應的實體
// 定時任務實體 @Data public class JobEntity { /** * 任務ID */ private Integer jobId; /** * bean名稱 */ private String beanName; /** * 方法名稱 */ private String methodName; /** * 方法參數 */ 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、讀取數據庫要執(zhí)行的任務
在程序啟動的時候,讀取數據庫,并創(chuàng)建要執(zhí)行任務
/** * @description: 初始化數據庫任務,可以再程啟動的時候加載數據庫里的任務 * @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) { // 初始加載數據庫里狀態(tài)為正常的定時任務 /* 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("定時任務已加載完畢..."); }*/ } }
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- springboot定時任務SchedulingConfigurer異步多線程實現(xiàn)方式
- SpringBoot注解@EnableScheduling定時任務詳細解析
- SpringBoot使用Scheduling實現(xiàn)定時任務的示例代碼
- springboot通過SchedulingConfigurer實現(xiàn)多定時任務注冊及動態(tài)修改執(zhí)行周期(示例詳解)
- springboot項目使用SchedulingConfigurer實現(xiàn)多個定時任務的案例代碼
- SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務多機器部署問題(推薦)
- SpringBoot Scheduling定時任務的示例代碼
相關文章
詳解Java編程中static關鍵字和final關鍵字的使用
這篇文章主要介紹了詳解Java編程中static關鍵字和final關鍵字的使用,是Java入門學習中的基礎知識,需要的朋友可以參考下2015-09-09SpringMVC接收java.util.Date類型數據的2種方式小結
這篇文章主要介紹了使用SpringMVC接收java.util.Date類型數據的2種方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Java實現(xiàn)上傳和下載功能(支持多個文件同時上傳)
這篇文章主要介紹了Java實現(xiàn)上傳和下載功能,支持多個文件同時上傳,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-12-12記一次集成swagger2(Knife4j)在線文檔提示:Knude4j文檔請求異常的解決辦法
Knife4j是一個集Swagger2 和 OpenAPI3為一體的增強解決方案,下面這篇文章主要給大家介紹了關于一次集成swagger2(Knife4j)在線文檔提示:Knude4j文檔請求異常的解決辦法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-02-02SpringBoot集成Aviator實現(xiàn)參數校驗的代碼工程
Aviator是一個高性能、輕量級的java語言實現(xiàn)的表達式求值引擎,主要用于各種表達式的動態(tài)求值,本文給大家詳細介紹了SpringBoot集成Aviator實現(xiàn)參數校驗的方法,并通過代碼示例講解的非常詳細,需要的朋友可以參考下2024-11-11