spring動(dòng)態(tài)控制定時(shí)任務(wù)的實(shí)現(xiàn)
在spring框架中,對(duì)于簡(jiǎn)單的定時(shí)任務(wù),可以使用 @Scheduled 注解實(shí)現(xiàn),在實(shí)際項(xiàng)目中,經(jīng)常需要?jiǎng)討B(tài)的控制定時(shí)任務(wù),比如通過(guò)接口增加、啟動(dòng)、停止、刪除定時(shí)任務(wù),動(dòng)態(tài)的改變定時(shí)任務(wù)的執(zhí)行時(shí)間等。
我們可以通過(guò)編碼的方式動(dòng)態(tài)控制定時(shí)任務(wù),具體的代碼參照 示例項(xiàng)目 https://github.com/qihaiyan/springcamp/tree/master/spring-dynamic-scheduler
一、概述
在spring框架可以通過(guò) CronTask 和 TaskScheduler 動(dòng)態(tài)控制定時(shí)任務(wù),實(shí)現(xiàn)定時(shí)任務(wù)的動(dòng)態(tài)更新,比如修改定時(shí)任務(wù)的執(zhí)行時(shí)間,這個(gè)是 @Scheduled 無(wú)法實(shí)現(xiàn)的。采用編碼控制動(dòng)態(tài)任務(wù)的方式,我們還可以把動(dòng)態(tài)任務(wù)執(zhí)行信息保存到數(shù)據(jù)庫(kù)中,通過(guò)數(shù)據(jù)庫(kù)里的任務(wù)配置數(shù)據(jù)來(lái)動(dòng)態(tài)控制定時(shí)任務(wù),也可以通過(guò)接口來(lái)動(dòng)態(tài)控制定時(shí)任務(wù)。
二、配置定時(shí)任務(wù)
首先,同 @Scheduled 注解的方式一樣,動(dòng)態(tài)控制定時(shí)任務(wù)也需要使用 @EnableScheduling 注解來(lái)開啟定時(shí)任務(wù)功能:
然后通過(guò)實(shí)現(xiàn) SchedulingConfigurer 接口來(lái)對(duì)動(dòng)態(tài)任務(wù)進(jìn)行配置:
@Component public class MyScheduler implements SchedulingConfigurer { private ScheduledTaskRegistrar taskRegistrar; private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>(); @Override public void configureTasks(@NonNull ScheduledTaskRegistrar taskRegistrar) { ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threads threadPoolTaskScheduler.setThreadNamePrefix("sys-scheduler"); threadPoolTaskScheduler.initialize(); this.taskRegistrar = taskRegistrar; this.taskRegistrar.setTaskScheduler(threadPoolTaskScheduler); } @PreDestroy public void destroy() { this.taskRegistrar.destroy(); } }
通過(guò)上面的代碼,我們就啟用了動(dòng)態(tài)任務(wù)的基本能力,為動(dòng)態(tài)任務(wù)指定了執(zhí)行線程池。
三、動(dòng)態(tài)更新定時(shí)任務(wù)
更新定時(shí)任務(wù)通過(guò) CronTask 和 TaskScheduler 來(lái)實(shí)現(xiàn),我們新增一個(gè)注冊(cè)定時(shí)任務(wù)的方法:
public void registerTask(TaskData taskData) { //如果配置一致,則不需要重新創(chuàng)建定時(shí)任務(wù) if (scheduledFutures.containsKey(taskData.getId()) && cronTasks.get(taskData.getId()).getExpression().equals(taskData.getExpression())) { return; } //如果策略執(zhí)行時(shí)間發(fā)生了變化,則取消當(dāng)前策略的任務(wù) if (scheduledFutures.containsKey(taskData.getId())) { scheduledFutures.remove(taskData.getId()).cancel(false); cronTasks.remove(taskData.getId()); } CronTask task = new CronTask(taskData, taskData.getExpression()); TaskScheduler scheduler = taskRegistrar.getScheduler(); if (scheduler != null) { ScheduledFuture<?> future = scheduler.schedule(task.getRunnable(), task.getTrigger()); if (future != null) { scheduledFutures.put(taskData.getId(), future); } } }
我們新增了一個(gè) registerTask 方法用于注冊(cè)定時(shí)任務(wù),入?yún)⒅?TaskData 是定時(shí)任務(wù)的配置數(shù)據(jù),為了簡(jiǎn)單,我們把配置數(shù)據(jù)和執(zhí)行代碼放到了一起:
@Slf4j @Data @Entity public class TaskData implements Runnable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String expression; @Transient @Override public void run() { log.info("{} is running with expression {}", this.getName(), this.getExpression()); } }
核心代碼是創(chuàng)建一個(gè) CronTask 對(duì)象,該對(duì)象包含兩個(gè)參數(shù):Runnable 方法和 cron 表達(dá)式。
CronTask 對(duì)象創(chuàng)建好后,通過(guò) ScheduledTaskRegistrar 對(duì)定時(shí)任務(wù)進(jìn)行注冊(cè),注冊(cè)完成后,定時(shí)任務(wù)就會(huì)在cron表達(dá)式指定的時(shí)間點(diǎn)開始執(zhí)行了。
執(zhí)行的代碼就是 Runnable 參數(shù)指定的方法。
四、動(dòng)態(tài)停止定時(shí)任務(wù)
為了能夠動(dòng)態(tài)停止定時(shí)任務(wù),我們?cè)谧?cè)定時(shí)任務(wù)時(shí),把注冊(cè)結(jié)果放到了一個(gè)Map中:
private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>(); ScheduledFuture<?> future = scheduler.schedule(task.getRunnable(), task.getTrigger()); if (future != null) { scheduledFutures.put(taskData.getId(), future); }
新增停止定時(shí)任務(wù)的方法:
public void stop(Long id) { if (scheduledFutures.containsKey(id)) { scheduledFutures.remove(id).cancel(false); } }
該方法需要傳入定時(shí)任務(wù)的id,由于我們把定時(shí)任務(wù)信息保存到了 scheduledFutures 這個(gè)Map中,所以可以根據(jù)任務(wù)id參數(shù)查找到對(duì)應(yīng)的定時(shí)任務(wù)信息,然后調(diào)用對(duì)應(yīng)的 cancel
方法來(lái)停止定時(shí)任務(wù)。
五、通過(guò)接口控制定時(shí)任務(wù)
通過(guò)上面的步驟我們已經(jīng)具備了動(dòng)態(tài)控制定時(shí)任務(wù)的基本能力,下面增加接口來(lái)控制定時(shí)任務(wù):
@EnableScheduling @SpringBootApplication @RestController public class DemoApplication { @Autowired private MyScheduler myScheduler; @Autowired private TaskDataRepository taskDataRepository; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @RequestMapping("/register") public TaskData register( String name, @RequestParam(name = "expression", required = false, defaultValue = "0/1 * * * * ?") String expression ) { TaskData taskData = taskDataRepository.findOneByName(name).orElse(new TaskData()); taskData.setName(name); taskData.setExpression(expression); taskData = taskDataRepository.save(taskData); myScheduler.registerTask(taskData); return taskData; } @RequestMapping("/stop") public void stop(Long id) { taskDataRepository.findById(id).ifPresent(taskData -> { myScheduler.stop(id); }); } }
我們提供了 register 和 stop 兩個(gè)接口,這兩個(gè)接口會(huì)在改變動(dòng)態(tài)任務(wù)執(zhí)行數(shù)據(jù)時(shí),先將數(shù)據(jù)保存到數(shù)據(jù)庫(kù)中,對(duì)定時(shí)任務(wù)進(jìn)行持久化,避免程序重啟后定時(shí)任務(wù)都丟失。
程序啟動(dòng)后,我們首先調(diào)用 register 接口新增一個(gè)定時(shí)任務(wù):
http://localhost:8080/register?name=test
接口調(diào)用后,在日志中可以看到定時(shí)任務(wù)開始執(zhí)行了:
2024-01-07T18:02:09.003+08:00 INFO 23012 --- [ sys-scheduler5] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ? 2024-01-07T18:02:10.005+08:00 INFO 23012 --- [ sys-scheduler3] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ? 2024-01-07T18:02:11.012+08:00 INFO 23012 --- [ sys-scheduler3] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ?
再調(diào)用 stop 接口,通過(guò)日志可以發(fā)現(xiàn)定時(shí)任務(wù)停止了執(zhí)行:
http://localhost:8080/stop?id=1
到此這篇關(guān)于spring動(dòng)態(tài)控制定時(shí)任務(wù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)spring動(dòng)態(tài)控制定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot定時(shí)任務(wù)的實(shí)現(xiàn)詳解
- 利用SpringBoot解決多個(gè)定時(shí)任務(wù)阻塞的問(wèn)題
- spring配置定時(shí)任務(wù)的幾種方式總結(jié)
- springboot中設(shè)置定時(shí)任務(wù)的三種方法小結(jié)
- Spring定時(shí)任務(wù)@scheduled多線程使用@Async注解示例
- Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)
- SpringBoot中實(shí)現(xiàn)定時(shí)任務(wù)的4種方式詳解
- Spring中的Schedule動(dòng)態(tài)添加修改定時(shí)任務(wù)詳解
- SpringBoot中的定時(shí)任務(wù)和異步調(diào)用詳解
- SpringBoot實(shí)現(xiàn)設(shè)置動(dòng)態(tài)定時(shí)任務(wù)的方法詳解
- Spring定時(shí)任務(wù)注解@Scheduled詳解
相關(guān)文章
spring-data-elasticsearch @Field注解無(wú)效的完美解決方案
這篇文章主要介紹了spring-data-elasticsearch @Field注解無(wú)效的完美解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java中null相關(guān)注解的實(shí)現(xiàn)
本文主要介紹了Java中null相關(guān)注解的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04SpringBoot參數(shù)校驗(yàn)的一些實(shí)戰(zhàn)應(yīng)用
這篇文章主要給大家介紹了關(guān)于SpringBoot參數(shù)校驗(yàn)的一些實(shí)戰(zhàn)應(yīng)用,包括使用內(nèi)置的參數(shù)校驗(yàn)注解、嵌套對(duì)象校驗(yàn)、分組校驗(yàn)以及自定義校驗(yàn)注解,通過(guò)這些方法,可以有效地提高系統(tǒng)的穩(wěn)定性和安全性,需要的朋友可以參考下2024-11-11SpringMVC中請(qǐng)求參數(shù)的獲取方式
這篇文章主要為大家介紹了SpringMVC中請(qǐng)求參數(shù)的獲取方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05java的Object里wait()實(shí)現(xiàn)原理講解
這篇文章主要介紹了java的Object里wait()實(shí)現(xiàn)原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09