背景介紹 我們在工作中難免會寫一些重覆性的代碼,所以需要我們具備一定的抽象能力,比如把共同的邏輯抽取到抽象類中,也可以通過一些工具類來避免冗餘代碼 今天這篇文章就是把一個調用服務的重試功能抽取出一個工具類,以備復用。這裡為了方便介紹,把調用服務簡化成方法的調用,被調用的 foo 方法如下: ~~~ ...
背景介紹
我們在工作中難免會寫一些重覆性的代碼,所以需要我們具備一定的抽象能力,比如把共同的邏輯抽取到抽象類中,也可以通過一些工具類來避免冗餘代碼
今天這篇文章就是把一個調用服務的重試功能抽取出一個工具類,以備復用。這裡為了方便介紹,把調用服務簡化成方法的調用,被調用的 foo 方法如下:
public static List<String> foo() {// 沒有顯示拋出異常
System.out.println("調用方法");
// 模擬拋出異常
System.out.println(1/0);
List<String> list = new ArrayList<>();
list.add("1");
return list;
}
調用方和重試邏輯如下:
List<String> result = null;
// 重試次數
int retryCount = 0;
// 調用服務的開關,預設打開
boolean callSwitch = true;
// 只要調用服務開關開著,並且重試次數不大於最大的重試次數,就調用服務
while (callSwitch && retryCount <= 3) {
try {
// 調用服務
result = foo();
// 省略了對結果的校驗,假設到了這裡就說明沒有問題,把調用服務開關關掉
callSwitch = false;
} catch (Exception e) {
// 發生了異常(比如超時,就需要重試了)
// 調用服務的開關打開
callSwitch = true;
retryCount++;
}
}
// 後面對 result 進行處理
其實上面的代碼就已經解決了,服務重試的邏輯,測試沒有問題後,可以提交代碼讓同事幫忙進行 CR 了,可是小朋同學看到這個代碼後,給了建議:
可以抽象一層,提出一個 retry 的工具類,這樣大家都可以簡單復用你的 retry 邏輯
抽象思考過程
白牙心想,也對哈,那就提出一個工具類吧,可是發了會兒呆,竟然沒有頭緒(反映出了抽象能力的薄弱,平時要多註意抽象能力的培養)。小朋見狀,給了一點提示,白牙立馬在鍵盤上噼里啪啦敲擊了起來,就有了下麵的工具類
主要依賴函數式介面 Supplier 和 BiFunction
public class RetryUtil {
public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) {
T result = null;
Exception exception = null;
int retryCount = 0;
boolean callMethod = true;
while (callMethod && retryCount <= maxRetryCount) {
try {
// 獲取調用服務的結果
result = supplier.get();
} catch (Exception e) {
// 如果重試次數不小於最大重試次數,就拋出異常,我們把對異常的處理交給業務方
if (retryCount >= maxRetryCount) {
throw e;
}
exception = e;
}
// 對結果進行判斷
callMethod = consumer.apply(result, exception);
if (callMethod) {
retryCount++;
}
}
return result;
}
}
業務調用方的代碼如下:
List<String> result1 = retry(3,// 最大重試次數
()-> foo(),// 調用服務
(list, e) -> e != null || list == null || list.isEmpty());// 對結果處理
自測沒有問題後,又提交代碼讓小朋給 CR 一下,小朋凝視了會兒,就有了下麵的對話
小朋:“retry 方法沒有拋出異常”
白牙:“被調用的服務沒有顯示的拋出異常,這裡也就沒有拋出”
小朋:“那人如果有服務方顯示拋出異常呢?”
白牙:“我再改一版”
服務方顯示拋出了異常,這樣 retry 方法也得顯示拋出異常,但調用方就會顯示會處理的異常,如下所示:
public static List<String> foo() throws Exception{// 顯示拋出異常
System.out.println("調用方法");
// 模擬拋出異常
System.out.println(1/0);
List<String> list = new ArrayList<>();
list.add("1");
return list;
}
public class RetryUtil {
public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
// 省略...
}
出現這種情況是因為 Supplier 的 get 方法沒有拋出異常
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
既然你不支持,那就自己寫個唄,於是就有了下麵的 DspSupplier,它和 Supplier 的主要區別就是在 get 方法中顯示拋出了異常
@FunctionalInterface
interface DspSupplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get() throws Exception;
}
於是 retry 方法就變成了下麵這樣子
public class RetryUtil {
public static <T> T retry(int maxRetryCount, DspSupplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
// 省略...
}
使用了自定義的 Supplier 後,調用方就沒有 “Unhandled exception” 了
總結
我們平時再開發的過程中,可以嘗試去利用函數式介面去實現一些邏輯的抽取,做成一個工具類,供大家使用,簡化人力,也是對自己編碼能力的一個提升。
上面的案例比較簡單,但麻雀雖小,五臟俱全,也是一個不錯的體驗
歡迎關註公眾號 【每天曬白牙】,獲取最新文章,我們一起交流,共同進步!