Spring中任務(wù)調(diào)度之解讀@Scheduled和@Schedules注解的使用
前言
在現(xiàn)代應(yīng)用程序開(kāi)發(fā)中,執(zhí)行定時(shí)任務(wù)是一個(gè)常見(jiàn)的需求。無(wú)論是定期執(zhí)行批處理作業(yè)、發(fā)送電子郵件通知,還是清理無(wú)用數(shù)據(jù),定時(shí)任務(wù)在許多應(yīng)用中都扮演著重要角色。
Spring框架為處理這一需求提供了強(qiáng)大的工具,其中@Scheduled
和@Schedules
注解就像是這個(gè)領(lǐng)域的秘密武器。
這篇博客將帶你深入探討這兩個(gè)注解,解釋它們的工作原理,以及如何在Spring應(yīng)用程序中使用它們來(lái)管理各種定時(shí)任務(wù)。
第一部分:什么是定時(shí)任務(wù)
定時(shí)任務(wù)是一種在應(yīng)用程序中執(zhí)行預(yù)定任務(wù)或操作的機(jī)制。這些任務(wù)可以按照預(yù)定的時(shí)間、日期或周期性地執(zhí)行,通常用于執(zhí)行一些自動(dòng)化的操作,如數(shù)據(jù)備份、報(bào)告生成、系統(tǒng)維護(hù)等。
定時(shí)任務(wù)在應(yīng)用程序開(kāi)發(fā)中非常重要,原因如下:
- 自動(dòng)化處理: 定時(shí)任務(wù)允許開(kāi)發(fā)人員自動(dòng)執(zhí)行重復(fù)性或計(jì)劃性的任務(wù),而無(wú)需手動(dòng)干預(yù)。這可以提高工作效率,減少人為錯(cuò)誤的風(fēng)險(xiǎn),并節(jié)省時(shí)間和資源。
- 數(shù)據(jù)處理: 定時(shí)任務(wù)經(jīng)常用于數(shù)據(jù)處理,例如定期從外部數(shù)據(jù)源獲取數(shù)據(jù)、清洗數(shù)據(jù)、將數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)等。這確保了數(shù)據(jù)一致性和及時(shí)性。
- 系統(tǒng)維護(hù): 定時(shí)任務(wù)可以用于執(zhí)行系統(tǒng)維護(hù)任務(wù),如日志文件的清理、數(shù)據(jù)庫(kù)索引的重建、服務(wù)器性能監(jiān)控等。這有助于確保應(yīng)用程序的穩(wěn)定性和性能。
- 計(jì)劃任務(wù): 對(duì)于需要按計(jì)劃執(zhí)行的任務(wù),例如發(fā)送定期報(bào)告或通知,定時(shí)任務(wù)是理想的解決方案。這有助于確保任務(wù)按時(shí)執(zhí)行,不會(huì)被遺漏。
- 資源管理: 定時(shí)任務(wù)可以幫助有效管理系統(tǒng)資源。例如,可以按需啟動(dòng)和停止資源密集型任務(wù),以充分利用服務(wù)器資源,而不會(huì)導(dǎo)致資源浪費(fèi)。
在軟件開(kāi)發(fā)中,通常使用編程語(yǔ)言或框架提供的定時(shí)任務(wù)調(diào)度器或庫(kù)來(lái)創(chuàng)建和管理定時(shí)任務(wù)。這些定時(shí)任務(wù)通常會(huì)伴隨著代碼實(shí)現(xiàn),以確保任務(wù)的正確執(zhí)行,并且可以在需要時(shí)進(jìn)行監(jiān)控和日志記錄。
通過(guò)良好的注釋,可以使代碼更易于維護(hù)和擴(kuò)展,同時(shí)有助于其他開(kāi)發(fā)人員了解任務(wù)的目的和功能。
第二部分:@Scheduled和@Schedules注解詳解
當(dāng)涉及到Spring框架中的定時(shí)任務(wù)支持,有兩種主要的注解,即@Scheduled
和@Schedules
,它們?cè)试S您在應(yīng)用程序中執(zhí)行周期性任務(wù)。以下是更詳細(xì)的解釋:
@Scheduled注解
@Scheduled
注解用于將一個(gè)方法標(biāo)記為定時(shí)任務(wù),該方法將根據(jù)指定的時(shí)間表執(zhí)行。您可以將@Scheduled
注解應(yīng)用于Spring管理的Bean的方法。
常用的@Scheduled注解屬性
fixedRate
屬性:根據(jù)固定的頻率執(zhí)行任務(wù)。
@Scheduled(fixedRate = 5000) // 每隔5秒執(zhí)行一次
fixedDelay
屬性:任務(wù)完成后,等待一段固定的時(shí)間再執(zhí)行下一次。
@Scheduled(fixedDelay = 5000) // 任務(wù)執(zhí)行完后,等待5秒再執(zhí)行下一次
initialDelay
屬性:在應(yīng)用啟動(dòng)后,首次執(zhí)行任務(wù)前的延遲時(shí)間。
@Scheduled(initialDelay = 3000, fixedRate = 5000) // 啟動(dòng)后等待3秒,然后每隔5秒執(zhí)行一次
cron
屬性:使用Cron表達(dá)式定義任務(wù)的執(zhí)行時(shí)間,可以實(shí)現(xiàn)高度靈活的調(diào)度。
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2點(diǎn)執(zhí)行
@Schedules注解
@Schedules
注解是一個(gè)容器注解,允許同時(shí)指定多個(gè)@Scheduled
注解的配置。這對(duì)于定義多個(gè)不同時(shí)間表的定時(shí)任務(wù)非常有用。
您可以在同一個(gè)方法上應(yīng)用多個(gè)@Scheduled
注解,或者使用@Schedules
將它們封裝在一個(gè)容器內(nèi)。
@Schedules({ @Scheduled(fixedRate = 5000), // 每隔5秒執(zhí)行一次 @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2點(diǎn)執(zhí)行 }) public void myScheduledMethods() { // 定時(shí)任務(wù)執(zhí)行的代碼 }
這些注解允許您在Spring應(yīng)用程序中輕松地創(chuàng)建和管理定時(shí)任務(wù)。您可以根據(jù)您的需求選擇合適的時(shí)間表和任務(wù)調(diào)度策略。通過(guò)這種方式,您可以自動(dòng)執(zhí)行各種任務(wù),例如數(shù)據(jù)清理、通知生成、定期報(bào)告等。同時(shí),使用適當(dāng)?shù)淖⑨尯臀臋n,可以使代碼更具可讀性和可維護(hù)性。
cron表達(dá)式詳解
Cron表達(dá)式是一種靈活而強(qiáng)大的方式,用于定義定時(shí)任務(wù)的執(zhí)行時(shí)間。它通常由6或7個(gè)字段組成,分別表示秒、分鐘、小時(shí)、一個(gè)月中的哪一天、月份、一個(gè)星期中的哪一天,以及可選的年份字段。
Cron表達(dá)式的語(yǔ)法如下:
┌───────────── 秒 (0 - 59) │ ┌───────────── 分鐘 (0 - 59) │ │ ┌───────────── 小時(shí) (0 - 23) │ │ │ ┌───────────── 一個(gè)月中的哪一天 (1 - 31) │ │ │ │ ┌───────────── 月份 (1 - 12 或 JAN-DEC) │ │ │ │ │ ┌───────────── 一個(gè)星期中的哪一天 (0 - 6 或 SUN-SAT, 7表示SUN) │ │ │ │ │ │ ┌───────────── 年份 (可選) │ │ │ │ │ │ │ │ │ │ │ │ │ │ * * * * * * *
以下是對(duì)每個(gè)字段的詳細(xì)說(shuō)明:
- 秒 (0 - 59): 表示一分鐘內(nèi)的秒數(shù)。例如,0表示每分鐘的開(kāi)始時(shí)執(zhí)行任務(wù),30表示每分鐘的30秒時(shí)執(zhí)行任務(wù)。
- 分鐘 (0 - 59): 表示一小時(shí)內(nèi)的分鐘數(shù)。例如,0表示每小時(shí)的開(kāi)始時(shí)執(zhí)行任務(wù),30表示每小時(shí)的30分鐘時(shí)執(zhí)行任務(wù)。
- 小時(shí) (0 - 23): 表示一天內(nèi)的小時(shí)數(shù)。例如,0表示每天的午夜執(zhí)行任務(wù),12表示每天中午執(zhí)行任務(wù)。
- 一個(gè)月中的哪一天 (1 - 31): 表示一個(gè)月內(nèi)的具體日期。例如,1表示每月的第一天執(zhí)行任務(wù),15表示每月的15號(hào)執(zhí)行任務(wù)。
- 月份 (1 - 12 或 JAN-DEC): 表示一年內(nèi)的月份。您可以使用數(shù)字(1 - 12)或縮寫的月份名稱(JAN-DEC)。例如,1或JAN表示一月,12或DEC表示十二月。
- 一個(gè)星期中的哪一天 (0 - 6 或 SUN-SAT, 7表示SUN): 表示一周內(nèi)的具體日期。您可以使用數(shù)字(0 - 6,其中0表示星期日)或縮寫的星期名稱(SUN-SAT)。例如,1或MON表示星期一,7或SUN表示星期日。
- 年份 (可選): 此字段是可選的,用于指定任務(wù)執(zhí)行的年份。通常情況下,您不需要指定年份。
下面是一些示例Cron表達(dá)式:
*/5 * * * * ? 每隔5秒執(zhí)行一次 0 */1 * * * ? 每隔1分鐘執(zhí)行一次 0 0 5-15 * * ? 每天5-15點(diǎn)整點(diǎn)觸發(fā) 0 0/3 * * * ? 每三分鐘觸發(fā)一次 0 0-5 14 * * ? 在每天下午2點(diǎn)到下午2:05期間的每1分鐘觸發(fā) 0 0/5 14 * * ? 在每天下午2點(diǎn)到下午2:55期間的每5分鐘觸發(fā) 0 0/5 14,18 * * ? 在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā) 0 0/30 9-17 * * ? 朝九晚五工作時(shí)間內(nèi)每半小時(shí) 0 0 10,14,16 * * ? 每天上午10點(diǎn),下午2點(diǎn),4點(diǎn) 0 0 12 ? * WED 表示每個(gè)星期三中午12點(diǎn) 0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五點(diǎn) 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44觸發(fā) 0 15 10 ? * MON-FRI 周一至周五的上午10:15觸發(fā) 0 0 23 L * ? 每月最后一天23點(diǎn)執(zhí)行一次 0 15 10 L * ? 每月最后一日的上午10:15觸發(fā) 0 15 10 ? * 6L 每月的最后一個(gè)星期五上午10:15觸發(fā) 0 15 10 * * ? 2005 2005年的每天上午10:15觸發(fā) 0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一個(gè)星期五上午10:15觸發(fā) 0 15 10 ? * 6#3 每月的第三個(gè)星期五上午10:15觸發(fā) 30 * * * * ? 每半分鐘觸發(fā)任務(wù) 30 10 * * * ? 每小時(shí)的10分30秒觸發(fā)任務(wù) 30 10 1 * * ? 每天1點(diǎn)10分30秒觸發(fā)任務(wù) 30 10 1 20 * ? 每月20號(hào)1點(diǎn)10分30秒觸發(fā)任務(wù) 30 10 1 20 10 ? * 每年10月20號(hào)1點(diǎn)10分30秒觸發(fā)任務(wù) 30 10 1 20 10 ? 2011 2011年10月20號(hào)1點(diǎn)10分30秒觸發(fā)任務(wù) 30 10 1 ? 10 * 2011 2011年10月每天1點(diǎn)10分30秒觸發(fā)任務(wù) 30 10 1 ? 10 SUN 2011 2011年10月每周日1點(diǎn)10分30秒觸發(fā)任務(wù) 15,30,45 * * * * ? 每15秒,30秒,45秒時(shí)觸發(fā)任務(wù) 15-45 * * * * ? 15到45秒內(nèi),每秒都觸發(fā)任務(wù) 15/5 * * * * ? 每分鐘的每15秒開(kāi)始觸發(fā),每隔5秒觸發(fā)一次 15-30/5 * * * * ? 每分鐘的15秒到30秒之間開(kāi)始觸發(fā),每隔5秒觸發(fā)一次 0 0/3 * * * ? 每小時(shí)的第0分0秒開(kāi)始,每三分鐘觸發(fā)一次 0 15 10 ? * MON-FRI 星期一到星期五的10點(diǎn)15分0秒觸發(fā)任務(wù) 0 15 10 L * ? 每個(gè)月最后一天的10點(diǎn)15分0秒觸發(fā)任務(wù) 0 15 10 LW * ? 每個(gè)月最后一個(gè)工作日的10點(diǎn)15分0秒觸發(fā)任務(wù) 0 15 10 ? * 5L 每個(gè)月最后一個(gè)星期四的10點(diǎn)15分0秒觸發(fā)任務(wù) 0 15 10 ? * 5#3 每個(gè)月第三周的星期四的10點(diǎn)15分0秒觸發(fā)任務(wù)
Cron表達(dá)式非常靈活,可以滿足各種復(fù)雜的調(diào)度需求。在Spring中,您可以使用@Scheduled
注解中的cron
屬性來(lái)配置Cron表達(dá)式,以實(shí)現(xiàn)高度定制的定時(shí)任務(wù)。
第三部分:高級(jí)用法
在Spring框架中,您可以通過(guò)不同的方式傳遞參數(shù)給定時(shí)任務(wù)方法,并且可以實(shí)現(xiàn)異常處理和錯(cuò)誤處理策略。此外,您還可以啟用異步定時(shí)任務(wù)以提高性能。下面讓我詳細(xì)解釋這些高級(jí)用法:
1. 傳遞參數(shù)給定時(shí)任務(wù)方法
您可以通過(guò)@Scheduled
注解傳遞參數(shù)給定時(shí)任務(wù)方法。以下是一個(gè)示例:
@Service public class ScheduledTaskService { @Scheduled(fixedRate = 5000) public void taskWithParameters() { // 在定時(shí)任務(wù)方法中傳遞參數(shù) executeTask("Task with parameters", 42); } public void executeTask(String taskName, int value) { // 執(zhí)行任務(wù),并使用傳遞的參數(shù) System.out.println(taskName + " - Parameter value: " + value); } }
在這個(gè)示例中,taskWithParameters
方法是一個(gè)定時(shí)任務(wù),它使用executeTask
方法來(lái)執(zhí)行任務(wù),并傳遞了兩個(gè)參數(shù):任務(wù)名稱和一個(gè)整數(shù)值。
2. 異常處理和錯(cuò)誤處理策略
在定時(shí)任務(wù)方法中,您可以使用標(biāo)準(zhǔn)的異常處理機(jī)制來(lái)處理異常。Spring提供了一種方式,可以將定時(shí)任務(wù)方法包裝在一個(gè)try-catch
塊中,以處理可能拋出的異常。您也可以使用Spring的@Scheduled
注解的@ExceptionHandler
屬性來(lái)指定異常處理方法。
以下是一個(gè)示例,演示如何處理定時(shí)任務(wù)方法中的異常:
@Service public class ScheduledTaskService { @Scheduled(fixedRate = 5000) public void taskWithException() { try { // 可能會(huì)拋出異常的代碼 throw new RuntimeException("An error occurred"); } catch (Exception e) { // 異常處理邏輯 System.err.println("Exception caught: " + e.getMessage()); } } }
3. 啟用異步定時(shí)任務(wù)
要啟用異步定時(shí)任務(wù),您可以在定時(shí)任務(wù)方法上使用@Async
注解,并在Spring配置中啟用異步支持。以下是一個(gè)示例:
首先,確保您在Spring配置中啟用了異步支持:
<task:annotation-driven executor="taskExecutor" /> <task:executor id="taskExecutor" pool-size="5" />
然后,在定時(shí)任務(wù)方法上使用@Async
注解:
@Service public class ScheduledTaskService { @Async @Scheduled(fixedRate = 5000) public void asyncTask() { // 異步執(zhí)行的任務(wù) System.out.println("Async task is running..."); } }
這將使asyncTask
方法在一個(gè)單獨(dú)的線程中異步執(zhí)行,而不會(huì)阻塞主線程。這對(duì)于執(zhí)行耗時(shí)操作的定時(shí)任務(wù)非常有用,以提高應(yīng)用程序的性能和響應(yīng)速度。
請(qǐng)注意,啟用異步定時(shí)任務(wù)需要適當(dāng)?shù)呐渲煤途€程池管理,以確保任務(wù)的正確執(zhí)行和資源的合理利用。
第四部分:最佳實(shí)踐
以下是編寫可維護(hù)和高效的定時(shí)任務(wù)的最佳實(shí)踐:
清晰的命名和注釋:
- 為定時(shí)任務(wù)方法和類使用清晰、描述性的命名,以便其他開(kāi)發(fā)人員可以輕松理解其用途。
- 提供詳細(xì)的注釋,解釋每個(gè)定時(shí)任務(wù)的目的、參數(shù)和特殊考慮事項(xiàng)。
分離業(yè)務(wù)邏輯:
- 將定時(shí)任務(wù)方法保持簡(jiǎn)潔,只包含與定時(shí)任務(wù)本身相關(guān)的邏輯。
- 將任務(wù)的實(shí)際業(yè)務(wù)邏輯移到單獨(dú)的服務(wù)或組件中,以提高代碼的可測(cè)試性和可維護(hù)性。
錯(cuò)誤處理:
- 實(shí)現(xiàn)適當(dāng)?shù)腻e(cuò)誤處理策略,以處理可能出現(xiàn)的異常情況。
- 使用
try-catch
塊來(lái)捕獲異常,確保不會(huì)因異常而中斷整個(gè)應(yīng)用程序。
使用參數(shù):
- 如果定時(shí)任務(wù)需要參數(shù),請(qǐng)使用方法參數(shù)來(lái)傳遞它們,而不是硬編碼在方法中。這使得定時(shí)任務(wù)更通用且易于維護(hù)。
定時(shí)任務(wù)的調(diào)度策略:
- 謹(jǐn)慎選擇定時(shí)任務(wù)的調(diào)度策略(如
fixedRate
或cron
)以確保任務(wù)按照需求執(zhí)行。 - 考慮定時(shí)任務(wù)的并發(fā)性,以避免多個(gè)實(shí)例同時(shí)執(zhí)行相同的任務(wù)。
異步任務(wù):
- 使用
@Async
注解將耗時(shí)任務(wù)標(biāo)記為異步 - 以提高應(yīng)用程序的性能和響應(yīng)速度
監(jiān)控和日志:
- 實(shí)現(xiàn)適當(dāng)?shù)娜罩居涗洠栽试S在出現(xiàn)問(wèn)題時(shí)進(jìn)行故障排除。
- 使用監(jiān)控工具來(lái)跟蹤定時(shí)任務(wù)的執(zhí)行情況,以及任務(wù)是否按照計(jì)劃執(zhí)行。
測(cè)試:
- 編寫單元測(cè)試來(lái)驗(yàn)證定時(shí)任務(wù)的邏輯和正確性。
- 使用模擬或偽造對(duì)象來(lái)模擬定時(shí)任務(wù)的依賴項(xiàng),以便更容易進(jìn)行單元測(cè)試。
定時(shí)任務(wù)參數(shù)的外部配置:
- 避免硬編碼定時(shí)任務(wù)的參數(shù)。
- 使用外部配置文件或?qū)傩裕员阍诓恢匦戮幾g代碼的情況下修改定時(shí)任務(wù)的參數(shù)。
定時(shí)任務(wù)的可用性和穩(wěn)定性:
- 考慮應(yīng)對(duì)應(yīng)用程序重啟和故障恢復(fù),以確保定時(shí)任務(wù)的可用性和穩(wěn)定性。
- 使用數(shù)據(jù)庫(kù)或其他持久性存儲(chǔ)來(lái)記錄任務(wù)的執(zhí)行狀態(tài),以便在應(yīng)用程序崩潰后能夠正確恢復(fù)。
通過(guò)遵循這些最佳實(shí)踐,您可以編寫可維護(hù)、高效且穩(wěn)定的定時(shí)任務(wù),確保它們?cè)趹?yīng)用程序中按計(jì)劃執(zhí)行并符合業(yè)務(wù)需求。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Spring兩種任務(wù)調(diào)度Scheduled和Async的區(qū)別和應(yīng)用場(chǎng)景詳解
- 基于SpringBoot實(shí)現(xiàn)輕量級(jí)的動(dòng)態(tài)定時(shí)任務(wù)調(diào)度的方法
- SpringBoot實(shí)現(xiàn)分布式任務(wù)調(diào)度的詳細(xì)步驟
- SpringBoot之@Scheduled注解用法解讀
- SpringBoot?@Scheduled?Cron表達(dá)式使用方式
- SpringBoot項(xiàng)目使用@Scheduled注解實(shí)現(xiàn)定時(shí)任務(wù)的方法
- SpringBoot的@Scheduled和@Schedules區(qū)別小結(jié)
相關(guān)文章
IDEA編譯報(bào)錯(cuò):Error:java:無(wú)效的源發(fā)行版:17的解決辦法
IDEA里面裝了幾個(gè)版本的JDK,導(dǎo)入工程后時(shí)不時(shí)提示一下錯(cuò)誤,下面這篇文章主要給大家介紹了關(guān)于IDEA編譯報(bào)錯(cuò):Error:java:無(wú)效的源發(fā)行版:17的解決辦法,需要的朋友可以參考下2023-01-01利用Java設(shè)置Word文本框中的文字旋轉(zhuǎn)方向的實(shí)現(xiàn)方法
Word文檔中可添加文本框,并設(shè)置文本框?yàn)闄M向文本排列或是縱向文本排列,或者設(shè)置文本框中的文字旋轉(zhuǎn)方向等.通過(guò)Java程序代碼,也可以實(shí)現(xiàn)以上文本框的操作.下面以Java代碼示例展示具體的實(shí)現(xiàn)步驟.另外,可參考C#及VB.NET代碼的實(shí)現(xiàn)方法,需要的朋友可以參考下2021-06-06詳解Java 自動(dòng)裝箱與拆箱的實(shí)現(xiàn)原理
本篇文章主要介紹了詳解Java 自動(dòng)裝箱與拆箱的實(shí)現(xiàn)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Java實(shí)現(xiàn)兩人五子棋游戲(七) 屏幕提示信息
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)兩人五子棋游戲,屏幕提示游戲信息,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03Java開(kāi)發(fā)框架spring實(shí)現(xiàn)自定義緩存標(biāo)簽
這篇文章主要介紹了Java開(kāi)發(fā)框架spring實(shí)現(xiàn)自定義緩存標(biāo)簽的詳細(xì)代碼,感興趣的小伙伴們可以參考一下2015-12-12Java開(kāi)發(fā)HashMap?key必須實(shí)現(xiàn)hashCode?equals方法原理
這篇文章主要為大家介紹了Java開(kāi)發(fā)HashMap?key必須實(shí)現(xiàn)hashCode?equals方法原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03