Spring中的接口重試機制spring-retry之listeners參數(shù)解析
背景
上篇文章中我們簡單介紹了spring-retry的功能及簡單用法,但是注解@Retryable還有一個參數(shù)listeners我們沒有進行說明, 那么本篇文章我們詳細介紹一個這個參數(shù)的用法。
分析
由參數(shù)名字我們我們可以知道,這里面可以配置一些監(jiān)聽器。
那這些監(jiān)聽器該如何進行配置呢?首先我們分析源碼。
注解源碼
我們只保留這個參數(shù)的源碼,其他的省略掉了。
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Retryable { /** * Bean names of retry listeners to use instead of default ones defined in Spring * context * @return retry listeners bean names */ String[] listeners() default {}; }
說明:源碼中該參數(shù)的解釋:使用的重試監(jiān)聽器的Bean名稱,而不是在Spring上下文中定義的默認(rèn)名稱,我們可以大膽的猜測出 這個是一個特定的Bean,需要開發(fā)者自己定義。
并且可以接收的參數(shù)是數(shù)組形式,那么問題是如何定義呢?
處理注解源碼
我們上篇文章中提到,處理注解 @Retryable的類為:AnnotationAwareRetryOperationsInterceptor,那么我們就在這個類中找尋我們要的答案。
方法getListenersBeans() 源碼
private RetryListener[] getListenersBeans(String[] listenersBeanNames) { RetryListener[] listeners = new RetryListener[listenersBeanNames.length]; for (int i = 0; i < listeners.length; i++) { listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class); } return listeners; }
有上面的代碼我們可以知道,我們自己定義的監(jiān)聽器肯定和RetryListener有著某種關(guān)系。下面我們分析該類的源碼
RetryListener 源碼
/** * Interface for listener that can be used to add behaviour to a retry. Implementations of * {@link RetryOperations} can chose to issue callbacks to an interceptor during the retry * lifecycle. */ public interface RetryListener { /** * Called before the first attempt in a retry. For instance, implementers can set up * state that is needed by the policies in the {@link RetryOperations}. The whole * retry can be vetoed by returning false from this method, in which case a * {@link TerminatedRetryException} will be thrown. * @param <T> the type of object returned by the callback * @param <E> the type of exception it declares may be thrown * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @return true if the retry should proceed. */ <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback); /** * Called after the final attempt (successful or not). Allow the interceptor to clean * up any resource it is holding before control returns to the retry caller. * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @param throwable the last exception that was thrown by the callback. * @param <E> the exception type * @param <T> the return value */ <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); /** * Called after every unsuccessful attempt at a retry. * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @param throwable the last exception that was thrown by the callback. * @param <T> the return value * @param <E> the exception to throw */ <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); }
分析:首先這個是一個接口,那不用想了,開發(fā)者自定義的監(jiān)聽器,必然要實現(xiàn)這個接口。
實現(xiàn)
由于listeners這個參數(shù)可以是多個,并且入?yún)⑹且粋€數(shù)組,我們先來定義兩哥監(jiān)聽器。代碼如下
RetryListenerBean
@Slf4j public class RetryListenerBean implements RetryListener { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { log.info("執(zhí)行了 open 方法 "); return true; } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("執(zhí)行了 onError 方法,說明出現(xiàn)了異常"); } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("執(zhí)行了 close 方法 "); } }
RetryListenerTwoBean
@Slf4j public class RetryListenerTwoBean implements RetryListener { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { log.info("執(zhí)行了 第二個 open 方法 "); return true; } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("執(zhí)行了 第二個 onError 方法,說明出現(xiàn)了異常"); } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("執(zhí)行了 第二個 close 方法 "); } }
分析1:我們定義了自己的兩個監(jiān)聽器,在參數(shù)listeners進行配置,代碼如下:
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5),listeners = {"retryListenerBean", "retryListenerTwoBean"} )
分析2:那么框架是如何識別到我們配置的監(jiān)聽器呢?請看源碼
for (int i = 0; i < listeners.length; i++) { listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class); }
分析3:在上面的代碼中,我們發(fā)現(xiàn)了beanFactory#getBean(),那么一切真相大白。因此我們必須將自己定義的監(jiān)聽器 交由Spring 進行管理。所以我們需要將自己定義的監(jiān)聽器進行配置。
監(jiān)聽器配置
我們將自己定義的監(jiān)聽器進行配置,由Spring進行管理,配置代碼如下:
@Configuration public class Config { @Bean("retryListenerBean") public RetryListenerBean listenerBean(){ return new RetryListenerBean(); } @Bean("retryListenerTwoBean") public RetryListenerTwoBean listenerTwoBean(){ return new RetryListenerTwoBean(); } }
進行如上代碼配置后,就可以通過Bean的名稱通過getBean的方法進行獲取監(jiān)聽器的實例了。
測試
啟動項目,我們使用postman進行測試。
日志如下:
2023-07-23 14:37:07.102 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.controller.RetryController : Controller 請求入?yún)椋?22
2023-07-23 14:37:25.312 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean : 執(zhí)行了 open 方法
2023-07-23 14:37:25.313 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean : 執(zhí)行了 第二個 open 方法
2023-07-23 14:37:30.816 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?22
2023-07-23 14:37:31.285 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Sun Jul 23 14:37:31 CST 2023
2023-07-23 14:37:38.312 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean : 執(zhí)行了 第二個 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:37:38.313 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean : 執(zhí)行了 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?22
2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Sun Jul 23 14:37:54 CST 2023
2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean : 執(zhí)行了 第二個 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean : 執(zhí)行了 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:38:02.498 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?22
2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Sun Jul 23 14:38:02 CST 2023
2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean : 執(zhí)行了 第二個 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean : 執(zhí)行了 onError 方法,說明出現(xiàn)了異常
2023-07-23 14:38:02.500 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 異常出現(xiàn)后的回調(diào)操作,入?yún)椋?22,當(dāng)前時間為:Sun Jul 23 14:38:02 CST 2023
2023-07-23 14:38:02.501 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean : 執(zhí)行了 第二個 close 方法
2023-07-23 14:38:02.502 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean : 執(zhí)行了 close 方法
以上的日志,可以看出來我們定義的監(jiān)聽器中的日志輸出,監(jiān)聽器的邏輯進行了執(zhí)行。
小結(jié)
由監(jiān)聽器的代碼,其中包括三個方案open、onError和close我們結(jié)合日志的輸出順序。
首先在執(zhí)行我們的業(yè)務(wù)邏輯之前,先執(zhí)行 open方案,相當(dāng)于一個前置攔截器,我們可以在這個方法中實現(xiàn)一些前置的邏輯操作。
遇到異常的情況會執(zhí)行onError方法。最終會 執(zhí)行close方法。因此我們在不同的階段利用這三個方法可以實現(xiàn)我們的想要的業(yè)務(wù)邏輯。
到此這篇關(guān)于Spring中的接口重試機制spring-retry之listeners參數(shù)解析的文章就介紹到這了,更多相關(guān)spring-retry之listeners參數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MybatisPlus實現(xiàn)真正批量插入的詳細步驟
在數(shù)據(jù)庫操作中,批量插入是提升效率的重要手段,MyBatis-Plus提供了多種批量插入方法,但默認(rèn)的saveBatch方法效率并不高,文章介紹了通過手動拼接SQL、使用IService接口以及自定義insertBatchSomeColumn方法進行優(yōu)化,以實現(xiàn)更高效的批量插入,并給出了性能優(yōu)化建議2024-10-10IDEA 2020.1 版自動導(dǎo)入MAVEN依賴的方法(新版MAVEN無法自動導(dǎo)入/更新POM依賴、MAVEN設(shè)置自動更
這篇文章主要介紹了IDEA 2020.1 版自動導(dǎo)入MAVEN依賴的方法(新版MAVEN無法自動導(dǎo)入/更新POM依賴、MAVEN設(shè)置自動更新、自動更新快捷鍵),需要的朋友可以參考下2020-08-08IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實例解析
這篇文章主要介紹了IntellJ IDEA JAVA代碼任務(wù)標(biāo)記實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07深入了解SpringBoot中@ControllerAdvice的介紹及三種用法
這篇文章主要為大家詳細介紹了SpringBoot中@ControllerAdvice的介紹及三種用法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-02-02Java命令設(shè)計模式優(yōu)雅解耦命令和執(zhí)行提高代碼可維護性
本文介紹了Java命令設(shè)計模式,它將命令請求封裝成對象,以達到解耦命令請求和執(zhí)行者的目的,從而提高代碼可維護性。本文詳細闡述了該模式的設(shè)計原則、實現(xiàn)方法和優(yōu)缺點,并提供了實際應(yīng)用場景和代碼示例,幫助讀者深入理解和應(yīng)用該模式2023-04-04在Java中動態(tài)執(zhí)行字符串代碼的方法小結(jié)
在Java編程中,靜態(tài)編譯的特性通常不允許我們直接執(zhí)行運行時生成的代碼,然而,有時我們需要動態(tài)地生成并執(zhí)行代碼片段,本文將詳細介紹如何在Java中運行一段字符串代碼,并提供詳細的代碼案例和運行結(jié)果,需要的朋友可以參考下2024-08-08Java?Bean轉(zhuǎn)Map的那些踩坑實戰(zhàn)
項目中有時會遇到Map轉(zhuǎn)Bean,Bean轉(zhuǎn)Map的情況,下面這篇文章主要給大家介紹了關(guān)于Java?Bean轉(zhuǎn)Map那些踩坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2022-07-07