一文帶你掌握SpringBoot中常見定時任務(wù)的實現(xiàn)
引言
在現(xiàn)代軟件開發(fā)中,定時任務(wù)是一種常見的需求,用于執(zhí)行周期性的任務(wù)或在特定的時間點執(zhí)行任務(wù)。這些任務(wù)可能涉及數(shù)據(jù)同步、數(shù)據(jù)備份、報表生成、緩存刷新等方面,對系統(tǒng)的穩(wěn)定性和可靠性有著重要的影響。Spring Boot
提供了強大且簡單的定時任務(wù)功能,使開發(fā)人員能夠輕松地管理和執(zhí)行這些任務(wù)。
本文將介紹 Spring Boot
中定時任務(wù)的基本用法、高級特性以及最佳實踐,幫助開發(fā)人員更好地理解和應(yīng)用定時任務(wù),提高系統(tǒng)的穩(wěn)定性和可靠性。
SpringBoot中的定時任務(wù)
SpringBoot
中的定時任務(wù)主要通過@Scheduled
注解以及SchedulingConfigurer
接口實現(xiàn)。
@Scheduled注解
@Scheduled
注解是Spring
提供的一個注解,用于標(biāo)記方法作為定時任務(wù)執(zhí)行。通過 @Scheduled
注解,開發(fā)人員可以輕松地配置方法在指定的時間間隔或時間點執(zhí)行,實現(xiàn)各種定時任務(wù)需求。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(Schedules.class) public @interface Scheduled { String cron() default ""; long fixedDelay() default -1; long fixedRate() default -1; long initialDelay() default -1; }
以上為@Scheduled
源碼中關(guān)鍵屬性,各屬性含義如下:
cron: 接受標(biāo)準(zhǔn)的Unix Cron表達式,用于定義復(fù)雜的計劃執(zhí)行時間。
/** * cron屬性可以設(shè)置指定時間執(zhí)行,cron表達式跟linux一樣 */ @Scheduled(cron = "0 45 14 ? * *") public void fixTimeExecution() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); }
fixedRate: 以固定的頻率執(zhí)行任務(wù),指定兩次執(zhí)行之間的間隔時間(單位是毫秒)。
/** * fixedRate屬性設(shè)置每隔固定時間執(zhí)行 */ @Scheduled(fixedRate = 5000) public void reportCurrentTime() { System.out.println("每隔五秒執(zhí)行一次" + dateFormat.format(new Date())); }
fixedDelay:在每次任務(wù)完成后等待一定的時間再進行下一次執(zhí)行,指定連續(xù)執(zhí)行之間的延遲時間。
/** * 上一次任務(wù)執(zhí)行完成之后10秒后在執(zhí)行 */ @Scheduled(fixedDelay = 10000) public void runWithFixedDelay() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); }
initialDelay:首次執(zhí)行前的延遲時間。
/** * 初始延遲1秒后開始,然后每10秒執(zhí)行一次 */ @Scheduled(initialDelay=1000, fixedDelay=10000) public void executeWithInitialAndFixedDelay() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); }
這里要注意fixedRate與fixedDelay的區(qū)別:fixedRate
是基于任務(wù)開始執(zhí)行的時間點來計算下一次任務(wù)開始執(zhí)行的時間,因此任務(wù)的執(zhí)行時間間隔是相對固定的,不受到任務(wù)執(zhí)行時間的影響。如果指定的時間間隔小于任務(wù)執(zhí)行的實際時間,則任務(wù)可能會并發(fā)執(zhí)行。而fixedDelay
是基于任務(wù)執(zhí)行完成的時間點來計算下一次任務(wù)開始執(zhí)行的時間,因此任務(wù)的執(zhí)行時間間隔是相對不規(guī)則的,受到任務(wù)執(zhí)行時間的影響。
SpringBoot
支持同時定義多個定時任務(wù)方法,每個方法可以使用不同的參數(shù)配置,以滿足不同的定時任務(wù)需求。同時,我們必須在配置類中使用@EnableScheduling
注解開啟定時任務(wù)。
@Configuration @EnableScheduling public class ScheduledTaskConfig { }
或者
@EnableScheduling @SpringBootApplication public class SpringBootBaseApplication { public static void main(String[] args) { SpringApplication.run(SpringBootBaseApplication.class, args); } }
在SpringBoot應(yīng)用程序中,除了在代碼中使用注解配置定時任務(wù)外,還可以通過配置文件來配置定時任務(wù)的執(zhí)行規(guī)則。這種方式更加靈活,可以在不修改源代碼的情況下,動態(tài)調(diào)整定時任務(wù)的執(zhí)行規(guī)則。比如我們在application.properties
中配置@Scheduled
的屬性:
custom.scheduled.cron = 0/5 * * * * ? custom.scheduled.fixedRate=5000 custom.scheduled.fixedDelay=10000 custom.scheduled.initialDelay=1000
然后在@Scheduled
的方法使用屬性配置定時任務(wù)執(zhí)行頻率。
@Service public class DemoScheduledTaskService { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); /** * fixedRate屬性設(shè)置每隔固定時間執(zhí)行 */ @Scheduled(fixedRateString = "${custom.scheduled.fixedRate}") public void reportCurrentTime() { System.out.println("每隔五秒執(zhí)行一次" + dateFormat.format(new Date())); } /** * cron屬性可以設(shè)置指定時間執(zhí)行,cron表達式跟linux一樣 */ @Scheduled(cron = "${custom.scheduled.cron}") public void fixTimeExecution() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); } /** * 上一次任務(wù)執(zhí)行完成之后10秒后在執(zhí)行 */ @Scheduled(fixedDelayString = "${custom.scheduled.fixedDelay}") public void runWithFixedDelay() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); } /** * 初始延遲1秒后開始,然后每10秒執(zhí)行一次 */ @Scheduled(initialDelayString = "${custom.scheduled.initialDelay}", fixedDelayString = "${custom.scheduled.fixedDelay}") public void executeWithInitialAndFixedDelay() { System.out.println("指定時間 "+dateFormat.format(new Date())+"執(zhí)行"); } }
注意,這里使用屬性來指定任務(wù)執(zhí)行頻率時,要通過@Scheduled的fixedRateString、fixedDelayString、initialDelayString三個可以指定字符串的值的屬性去指定,效果等同于long類型的屬性。
通過配置文件配置定時任務(wù)具有很高的靈活性,可以在不重新編譯和部署應(yīng)用程序的情況下,隨時調(diào)整定時任務(wù)的執(zhí)行規(guī)則。同時,也可以根據(jù)不同的環(huán)境(例如開發(fā)、測試、生產(chǎn))配置不同的定時任務(wù)規(guī)則,以滿足不同環(huán)境下的需求。這種方式可以有效地解耦定時任務(wù)的配置和業(yè)務(wù)代碼,提高系統(tǒng)的靈活性和可維護性。
另外,如果希望定時任務(wù)能夠異步執(zhí)行,不阻塞主線程,可以在方法上同時加上@Async
注解,這樣各任務(wù)就可以異步執(zhí)行了。有關(guān)SpringBoot中使用@Async
的講解,請移步:
雖然 @Scheduled
注解是一個方便的方式來定義定時任務(wù),但它也存在一些弊端。因為任務(wù)的執(zhí)行計劃(如cron表達式)在編譯時被硬編碼,因此無法在運行時動態(tài)修改,除非重新部署。此外,@Scheduled注解對于配置不同的調(diào)度策略(如使用不同的線程池)顯得力不從心,而且默認情況下,@Scheduled任務(wù)在單線程環(huán)境下執(zhí)行,可能出現(xiàn)任務(wù)堆積的情況,尤其在任務(wù)量大或任務(wù)執(zhí)行時間長的情況下,而且這些任務(wù)可能會變得混亂和難以管理。定時任務(wù)的配置分散在各個任務(wù)方法中,不利于統(tǒng)一管理和維護。對于需要根據(jù)動態(tài)條件創(chuàng)建或銷毀定時任務(wù)的情況,@Scheduled
注解也無法滿足需求。
為了解決這些問題,可以使用SchedulingConfigurer
接口來動態(tài)地創(chuàng)建和管理定時任務(wù)。通過實現(xiàn) SchedulingConfigurer
接口,我們可以編寫代碼來動態(tài)地注冊和管理定時任務(wù),從而實現(xiàn)靈活的任務(wù)調(diào)度需求。接下來,我們將介紹如何使用SchedulingConfigurer
接口來創(chuàng)建定時任務(wù)。
SchedulingConfigurer接口
SchedulingConfigurer
接口是 Spring 提供的一個用于定時任務(wù)配置的擴展接口,它允許開發(fā)人員更細粒度地控制定時任務(wù)的執(zhí)行。通過實現(xiàn)SchedulingConfigurer
接口,可以自定義任務(wù)調(diào)度器(TaskScheduler
),配置線程池等參數(shù),以滿足不同場景下的定時任務(wù)需求。
@Configuration @EnableScheduling public class CustomSchedulingConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 定時任務(wù)邏輯 } }
通過實現(xiàn)SchedulingConfigurer
接口,重寫configureTasks
方法,自定義任務(wù)調(diào)度器的配置。此外我們還可以配置線程池,用于控制定時任務(wù)執(zhí)行時的線程數(shù)量、并發(fā)性等參數(shù)。
@Bean(destroyMethod = "shutdown") public ThreadPoolTaskScheduler threadPoolTaskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(5); // 設(shè)置線程池大小 scheduler.setThreadNamePrefix("scheduled-task-"); // 設(shè)置線程名稱前綴 scheduler.setAwaitTerminationSeconds(60); // 設(shè)置終止等待時間 // 設(shè)置處理拒絕執(zhí)行的任務(wù)異常 scheduler.setRejectedExecutionHandler((r, executor) -> log.error("Task rejected", r)); // 處理定時任務(wù)執(zhí)行過程中拋出的未捕獲異常 scheduler.setErrorHandler(e -> log.error("Error in scheduled task", e)); return scheduler; }
然后將自定義的ThreadPoolTaskScheduler
設(shè)置到ScheduledTaskRegistrar
中去:
@Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 定時任務(wù)邏輯 taskRegistrar.setTaskScheduler(threadPoolTaskScheduler()); }
有關(guān)線程池的配置參數(shù)講解,請移步:
通過SchedulingConfigurer
接口,可以更靈活地配置任務(wù)調(diào)度器和定時任務(wù)的執(zhí)行規(guī)則,比如動態(tài)注冊定時任務(wù)、動態(tài)修改任務(wù)執(zhí)行規(guī)則等。
動態(tài)添加定時任務(wù)
在SchedulingConfigurer
的configureTasks
方法中,我們可以根據(jù)業(yè)務(wù)需求,從數(shù)據(jù)庫、配置文件或其它動態(tài)來源獲取定時任務(wù)的信息(如Cron表達式、任務(wù)執(zhí)行類等),然后創(chuàng)建對應(yīng)的Runnable
或Callable
實例,并結(jié)合Trigger
(如CronTrigger
)將其添加到調(diào)度器中。相比@Scheduled
注解,這種方式能夠在應(yīng)用運行時隨時添加新的定時任務(wù)。
@Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ThreadPoolTaskScheduler scheduler = threadPoolTaskScheduler(); taskRegistrar.setTaskScheduler(scheduler); List<CronTaskInfo> tasksFromDB = listTasksFromDatabase(); for (CronTaskInfo task : tasksFromDB) { Runnable taskRunner = new MyTaskExecutor(task.getTaskData()); CronTrigger cronTrigger = new CronTrigger(task.getCronExpression()); scheduler.schedule(taskRunner, cronTrigger); } }
關(guān)于這里在應(yīng)用運行時,動態(tài)的添加新的任務(wù),我們可以通過事件驅(qū)動,輪訓(xùn)檢查,消息隊列等多種方式,監(jiān)聽到數(shù)據(jù)庫中或者配置文件中新增任務(wù)信息,然后通過SchedulingConfigurer
接口動態(tài)創(chuàng)建定時任務(wù)。而這種方式是@Scheduled注解做不到的。
修改定時任務(wù)規(guī)則
當(dāng)任務(wù)的執(zhí)行規(guī)則需要動態(tài)變更時,同樣可以在configureTasks
方法中實現(xiàn)。例如,從數(shù)據(jù)庫獲取最新的Cron表達式,然后取消當(dāng)前任務(wù)并重新添加新的任務(wù)實例。需要注意的是,取消已有任務(wù)通常需要持有對該任務(wù)的引用,例如使用Scheduler
提供的unschedule
方法。
// 假設(shè)我們有一個方法用于獲取更新后的任務(wù)信息 CronTaskInfo updatedTask = getUpdatedTaskInfoFromDatabase(); // 取消舊的任務(wù)(需要知道舊任務(wù)的TriggerKey) TriggerKey triggerKey = ...; // 獲取舊任務(wù)的TriggerKey scheduler.unschedule(triggerKey); // 創(chuàng)建新任務(wù)并設(shè)置新的Cron表達式 MyTaskExecutor taskExecutor = new MyTaskExecutor(updatedTask.getTaskData()); CronTrigger updatedCronTrigger = new CronTrigger(updatedTask.getCronExpression()); // 重新調(diào)度新任務(wù) scheduler.schedule(taskRunner, updatedCronTrigger);
另外,我們還可以通過添加任務(wù)時對其排序或設(shè)置優(yōu)先級等方式間接實現(xiàn)設(shè)置定時任務(wù)的執(zhí)行順序。
通過實現(xiàn)SchedulingConfigurer
接口,我們可以擁有對定時任務(wù)調(diào)度的更多控制權(quán),比如自定義線程池、動態(tài)添加任務(wù)以及調(diào)整任務(wù)執(zhí)行策略。這種靈活性使得在復(fù)雜環(huán)境下,特別是需要動態(tài)管理定時任務(wù)時,SchedulingConfigurer
成為了理想的選擇。
其他第三方任務(wù)調(diào)度框架
除了使用Spring框架提供的 @Scheduled
注解和SchedulingConfigurer
接口外,還有許多第三方的任務(wù)調(diào)度庫可供選擇。這些庫通常提供了更多的功能和靈活性,以滿足各種復(fù)雜的任務(wù)調(diào)度需求。以下是一些常見的第三方任務(wù)調(diào)度庫:
Quartz Scheduler:
Quartz
是一個功能強大且靈活的任務(wù)調(diào)度庫,具有豐富的功能,如支持基于cron
表達式的任務(wù)調(diào)度、集群支持、作業(yè)持久化等。它可以與Spring
框架集成,并且被廣泛應(yīng)用于各種類型的任務(wù)調(diào)度應(yīng)用程序中。
Elastic Job:
Elastic Job
是一個分布式任務(wù)調(diào)度框架,可以輕松實現(xiàn)分布式任務(wù)調(diào)度和作業(yè)執(zhí)行。它提供了分布式任務(wù)執(zhí)行、作業(yè)依賴關(guān)系、作業(yè)分片等功能,適用于大規(guī)模的分布式任務(wù)調(diào)度場景。
xxl-job:
xxl-job
是一個分布式任務(wù)調(diào)度平臺,提供了可視化的任務(wù)管理界面和多種任務(wù)調(diào)度方式,如單機任務(wù)、分布式任務(wù)、定時任務(wù)等。它支持任務(wù)執(zhí)行日志、任務(wù)失敗重試、動態(tài)調(diào)整任務(wù)執(zhí)行策略等功能。
PowerJob:
PowerJob
是一個開源的分布式任務(wù)調(diào)度框架,由阿里巴巴集團開發(fā)并開源。PowerJob 提供了分布式、高可用的任務(wù)調(diào)度能力,支持多種任務(wù)類型,如定時任務(wù)、延時任務(wù)、流程任務(wù)等。
總結(jié)
定時任務(wù)在現(xiàn)代軟件開發(fā)中扮演著重要的角色,它們可以自動化執(zhí)行各種重復(fù)性的任務(wù),提高系統(tǒng)的效率和可靠性。SpringBoot提供了強大而靈活的定時任務(wù)功能,使我們能夠輕松地管理和執(zhí)行各種定時任務(wù)。通過@Scheduled
注解和SchedulingConfigurer
接口,我們可以根據(jù)需求配置定時任務(wù)的執(zhí)行規(guī)則,實現(xiàn)各種復(fù)雜的定時任務(wù)調(diào)度需求。我們可以充分利用SpringBoot中的定時任務(wù)功能,提高系統(tǒng)的穩(wěn)定性和可靠性,從而更好地滿足業(yè)務(wù)需求。
到此這篇關(guān)于一文帶你掌握SpringBoot中常見定時任務(wù)的實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java導(dǎo)入、導(dǎo)出excel用法步驟保姆級教程(附封裝好的工具類)
這篇文章主要介紹了Java導(dǎo)入、導(dǎo)出excel的相關(guān)資料,講解了使用Java和ApachePOI庫將數(shù)據(jù)導(dǎo)出為Excel文件,包括創(chuàng)建工作簿、工作表、行和單元格,設(shè)置樣式和字體,合并單元格,添加公式和下拉選擇等功能,需要的朋友可以參考下2025-03-03Java中資源加載的方法及Spring的ResourceLoader應(yīng)用小結(jié)
在Java開發(fā)中,資源加載是一個基礎(chǔ)而重要的操作,這篇文章主要介紹了深入理解Java中資源加載的方法及Spring的ResourceLoader應(yīng)用,本文通過實例代碼演示了通過ClassLoader和Class獲取資源的內(nèi)容,以及使用Spring的ResourceLoader加載多個資源的過程,需要的朋友可以參考下2024-01-01Java結(jié)合Kotlin實現(xiàn)寶寶年齡計算
這篇文章主要為大家介紹了Java結(jié)合Kotlin實現(xiàn)寶寶年齡計算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06springcloud項目快速開始起始模板的實現(xiàn)
本文主要介紹了springcloud項目快速開始起始模板思路的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12