Spring中的接口重試機(jī)制spring-retry之listeners參數(shù)解析
背景
上篇文章中我們簡單介紹了spring-retry的功能及簡單用法,但是注解@Retryable還有一個(gè)參數(shù)listeners我們沒有進(jìn)行說明, 那么本篇文章我們?cè)敿?xì)介紹一個(gè)這個(gè)參數(shù)的用法。
分析
由參數(shù)名字我們我們可以知道,這里面可以配置一些監(jiān)聽器。
那這些監(jiān)聽器該如何進(jìn)行配置呢?首先我們分析源碼。
注解源碼
我們只保留這個(gè)參數(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)名稱,我們可以大膽的猜測出 這個(gè)是一個(gè)特定的Bean,需要開發(fā)者自己定義。
并且可以接收的參數(shù)是數(shù)組形式,那么問題是如何定義呢?
處理注解源碼
我們上篇文章中提到,處理注解 @Retryable的類為:AnnotationAwareRetryOperationsInterceptor,那么我們就在這個(gè)類中找尋我們要的答案。
方法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);
}分析:首先這個(gè)是一個(gè)接口,那不用想了,開發(fā)者自定義的監(jiān)聽器,必然要實(shí)現(xiàn)這個(gè)接口。
實(shí)現(xiàn)
由于listeners這個(gè)參數(shù)可以是多個(gè),并且入?yún)⑹且粋€(gè)數(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í)行了 第二個(gè) open 方法 ");
return true;
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
log.info("執(zhí)行了 第二個(gè) onError 方法,說明出現(xiàn)了異常");
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
log.info("執(zhí)行了 第二個(gè) close 方法 ");
}
}
分析1:我們定義了自己的兩個(gè)監(jiān)聽器,在參數(shù)listeners進(jìn)行配置,代碼如下:
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5),listeners = {"retryListenerBean", "retryListenerTwoBean"} )
分析2:那么框架是如何識(shí)別到我們配置的監(jiān)聽器呢?請(qǐng)看源碼
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 進(jìn)行管理。所以我們需要將自己定義的監(jiān)聽器進(jìn)行配置。
監(jiān)聽器配置
我們將自己定義的監(jiān)聽器進(jìn)行配置,由Spring進(jìn)行管理,配置代碼如下:
@Configuration
public class Config {
@Bean("retryListenerBean")
public RetryListenerBean listenerBean(){
return new RetryListenerBean();
}
@Bean("retryListenerTwoBean")
public RetryListenerTwoBean listenerTwoBean(){
return new RetryListenerTwoBean();
}
}
進(jìn)行如上代碼配置后,就可以通過Bean的名稱通過getBean的方法進(jìn)行獲取監(jiān)聽器的實(shí)例了。
測試
啟動(dòng)項(xiàng)目,我們使用postman進(jìn)行測試。
日志如下:
2023-07-23 14:37:07.102 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.controller.RetryController : Controller 請(qǐng)求入?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í)行了 第二個(gè) open 方法
2023-07-23 14:37:30.816 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : Service 請(qǐng)求入?yún)椋?22
2023-07-23 14:37:31.285 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進(jìn)入測試方法,目前時(shí)間為: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í)行了 第二個(gè) 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 請(qǐng)求入?yún)椋?22
2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進(jìn)入測試方法,目前時(shí)間為: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í)行了 第二個(gè) 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 請(qǐng)求入?yún)椋?22
2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl : 進(jìn)入測試方法,目前時(shí)間為: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í)行了 第二個(gè) 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)前時(shí)間為: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í)行了 第二個(gè) 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)聽器的邏輯進(jìn)行了執(zhí)行。
小結(jié)
由監(jiān)聽器的代碼,其中包括三個(gè)方案open、onError和close我們結(jié)合日志的輸出順序。
首先在執(zhí)行我們的業(yè)務(wù)邏輯之前,先執(zhí)行 open方案,相當(dāng)于一個(gè)前置攔截器,我們可以在這個(gè)方法中實(shí)現(xiàn)一些前置的邏輯操作。
遇到異常的情況會(huì)執(zhí)行onError方法。最終會(huì) 執(zhí)行close方法。因此我們?cè)诓煌碾A段利用這三個(gè)方法可以實(shí)現(xiàn)我們的想要的業(yè)務(wù)邏輯。
到此這篇關(guān)于Spring中的接口重試機(jī)制spring-retry之listeners參數(shù)解析的文章就介紹到這了,更多相關(guān)spring-retry之listeners參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Security 安全框架應(yīng)用原理解析
這篇文章主要介紹了Spring Security 安全框架應(yīng)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07
Java和Rust實(shí)現(xiàn)JSON序列化互轉(zhuǎn)的解決方案詳解
這篇文章主要為大家詳細(xì)介紹了Java和Rust實(shí)現(xiàn)JSON序列化互轉(zhuǎn)的解決方案,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
SpringBoot+Vue前后端分離實(shí)現(xiàn)審核功能的示例
在實(shí)際開發(fā)中,審核功能是一個(gè)非常常用的功能,本文就來介紹一下使用SpringBoot+Vue前后端分離實(shí)現(xiàn)審核功能的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
Java中的封裝性(包含this關(guān)鍵字,構(gòu)造器等)
這篇文章主要介紹了Java中的封裝性(包含this關(guān)鍵字,構(gòu)造器等)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
深入剖析Java中的synchronized關(guān)鍵字
在 Java 程序中,我們可以利用 synchronized 關(guān)鍵字來對(duì)程序進(jìn)行加鎖,它既可以用來聲明一個(gè) synchronized 代碼塊,也可以直接標(biāo)記靜態(tài)方法或者實(shí)例方法,本文就帶大家深入了解Java中的synchronized關(guān)鍵字,感興趣的同學(xué)可以參考閱讀2023-06-06
解決java idea新建子目錄時(shí)命名不是樹形結(jié)構(gòu)的問題
這篇文章主要介紹了解決java idea新建子目錄時(shí)命名不是樹形結(jié)構(gòu)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
手?jǐn)]一個(gè)Spring?Boot?Starter并上傳到Maven中央倉庫
本文主要介紹了手?jǐn)]一個(gè)Spring?Boot?Starter并上傳到Maven中央倉庫,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
mybatis嵌套循環(huán)map方式(高級(jí)用法)
這篇文章主要介紹了mybatis嵌套循環(huán)map方式(高級(jí)用法),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
linux下idea、pycharm等輸入中文拼音時(shí)滿3個(gè)字母后無法繼續(xù)拼音輸入的問題
這篇文章主要介紹了linux下idea、pycharm等輸入中文拼音時(shí)滿3個(gè)字母后無法繼續(xù)拼音輸入的問題,本文通過圖文并茂的形式給大家分享解決方法,需要的朋友可以參考下2021-04-04

