Forest v1.5.3 正式版本釋出

2021-09-24 17:00:06

v1.5.3 本版釋出了,本次版本更新新增了較多功能,其中有許多重大更新。

重點更新內容

1. 新增Forest快捷介面

以前版本使用 Forest,必須先定義一個 interface 介面類,這種形式可以滿足大多數情況的場景。 但若想快速存取一個url可能顯得不合時宜。 所以本次更新新增了快捷介面,不用再從定義介面開始了。

它大概長這個樣子:


// Get請求
// 並以 String 型別接受資料
String str = Forest.get("/").executeAsString();

// Post請求
// 並以自定義的 MyResult 型別接受
MyResult myResult = Forest
   .post("/")
   .execute(MyResult.class);

// 通過 TypeRefernce 參照類傳遞泛型引數
// 就可以將響應資料以帶複雜泛型引數的型別接受了
Result<List<User>> userList = Forest
   .post("/")
   .execute(new TypeReferenceList<Result<List<User>>>() {});


// 定義各種引數
// 並以 Map 型別接受
Map<String, Object> map = Forest.post("/")
      .backend("okhttp3")        // 設定後端為 okhttp3
      .contentTypeJson()         // 設定 Content-Type 頭為 application/json
      .host("127.0.0.1")         // 設定地址的host為 127.0.0.1
      .port(8080)                // 設定地址的埠為 8080
      .addBody("a", 1)           // 新增 Body 項(鍵值對): a, 1
      .addBody("b", 2)           // 新增 Body 項(鍵值對:  b, 2
      .maxRetryCount(3)          // 設定請求最大重試次數為 3
      // 設定 onSuccess 回撥函數
      .onSuccess((data, req, res) -> { log.info("success!"); })
      // 設定 onError 回撥函數
      .onError((ex, req, res) -> { log.info("error!"); })
      // 設定請求成功判斷條件回撥函數
      .successWhen((req, res) -> res.noException() && res.statusOk())
      // 執行並返回Map資料型別物件
      .executeAsMap();

2. 請求成功條件/重試條件

2.1 @Success 註解

先要定義 SuccessWhen 介面的實現類

public class TestSuccessWhen implements SuccessWhen {

    /**
     * 請求成功條件
     * @param req Forest請求物件
     * @param res Forest響應物件
     * @return 是否成功
     */
    @Override
    public boolean successWhen(ForestRequest req, ForestResponse res) {
        // 沒有異常 並且 狀態碼在正常範圍 並且 狀態碼不等於203
        // 當然在這裡也可以寫其它條件,比如 通過 res.getData() 或 res.getConent() 獲取業務資料
        // 再更具業務資料判斷是否成功
        return res.noException() && res.statusOk() && res.statusCode() != 203;
    }
}

在Forest請求介面方法上掛上 @Success 註解

@Get("http://localhost:${port}/")
@Success(condition = TestSuccessWhen.class)
String getData();

若呼叫 getData() 後,返回的狀態碼為 203, 就會被認為是請求失敗,如果設定了重試次數大於0,就會去執行重試任務。 若沒有重試次數可用,則進入 onError 請求失敗流程

2.2 使用 @Retry 註解

先定義 RetryWhen 介面實現類

public class TestRetryWhen implements RetryWhen {

    /**
     * 請求重試條件
     * @param request Forest請求物件
     * @param response Forest響應物件
     * @return 是否重試
     */
    @Override
    public boolean retryWhen(ForestRequest request, ForestResponse response) {
        // 如果響應狀態碼為 203 就進行重試,儘管此時請求是成功的
        // 當然在這裡也可以寫其它條件,比如 通過 res.getData() 或 res.getConent() 獲取業務資料
        // 再更具業務資料判斷是否進行重試
        return response.statusIs(203);
    }

}

在Forest請求介面方法上掛上 @Retry 註解

// maxRetryCount 為最大重試次數
// maxRetryInterval 為最大重試時間間隔, 單位為毫秒
// condition 為請求重試條件,即自定義的 RetryWhen 介面實現類
@Get("http://localhost:${port}/")
@Retry(maxRetryCount = "3", maxRetryInterval = "10", condition = TestRetryWhen.class)
String sendData();

若呼叫 sendData() 後,返回的狀態碼為 203, 就會被認為需要重試,如果設定了重試次數大於0,就會去執行重試任務。 若沒有重試次數可用,則進入 onSuccess 請求成功流程

2.3 兩種重試的區別

可能有小夥伴有疑問,既然通過 SuccessWhen 成功條件判斷失敗後也可以觸發重試,那為何還要 RetryWhen 呢?

Forest的重試機制是這樣的:1. 先判定是否成功,失敗的話觸發重試 2. 如果是成功的,則判斷是否符合重試條件,符合的話也觸發重試.

這兩者的區別是:

因為請求失敗而重試的請求,當最後一次重試也是失敗的話,就會進入請求失敗流程(如呼叫 onError)

因為觸發重試條件而重試的請求,此時請求判斷是成功的,所以只要最後一次重試也是成功的,就會進入請求成功流程 (如呼叫 onSuccess)

簡單一句話描述: successWhen失敗就重試,retryWhen即便成功也重試

2.4 OnRetry 回撥函數

不管是哪一種重試方式,只要觸發了請求重試,都會在重試請傳送之前呼叫 OnRetry 回撥函數

可以在攔截器中實現 onRetry 回撥函數

public class TestRetryInterceptor implements Interceptor<Object> {

    /**
     * 在請重試前呼叫 onRetry 回撥函數
     * 
     * @param request Forest請求物件
     * @param response Forest響應物件
     */
    @Override
    public void onRetry(ForestRequest request, ForestResponse response) {
        // 將當前重試次數新增到 Forest 請求物件的附件中
        request.addAttachment("retry-interceptor", request.getCurrentRetryCount());
    }
}

將實現了 onRetry 方法的攔截器關聯到相關介面上

@BaseRequest(baseURL = "http://localhost:${port}/", interceptor = TestRetryInterceptor.class)
public interface RetryClient {

    @Get("/")
    @Retry(maxRetryCount = "${0}", maxRetryInterval = "${1}", condition = TestRetryWhen.class)
    ForestRequest<String> testRetryRequest(int retryCount, long retryInterval);

    @Get("/")
    @Retry(maxRetryCount = "${0}", maxRetryInterval = "${1}", condition = TestRetryWhen.class)
    String testRetry(int retryCount, long retryInterval, OnSuccess<String> onSuccess);
}

此時 RetryClient 介面下的任意一個方法觸發重試時,都會先呼叫 TestRetryInterceptor 攔截器類的 onRetry 方法。

其它新特性

新增特性:

  • feat: Forest快捷介面 (#I4893Q)
  • feat: 支援全域性變數動態繫結方法 (#I478N2)
  • feat: 支援參照properties的字串模板 (#I3P1QK)
  • feat: 支援獲取響應原因短語,即響應狀態文字 (#I4BJVF)
  • feat: 自定義組合註解 (#I4BISF)
  • feat: 可自定義請求是否成功的條件 (#I4AEMT)
  • feat: 可動態設定主機地址和埠號 (#I4AEJ8)
  • feat: 自定義重試條件 (#I493N3)
  • feat: 新增 OnRetry 回撥函數 (#I493N6)
  • feat: 新增 @Headers 註解 (#I4BJQ6)
  • feat: Forest請求介面繼承規則 (#I4B0N7)
  • feat: 自動重定向控制 (#I4B0FM)
  • feat: 全域性變數支援動態繫結方法 (#I478N2)
  • feat: 在請求紀錄檔中顯示後端框架名稱 (#I4AKTD)
  • feat: 新建forest-mock子專案 (#I468JB)

Fix的Bug:

  • fix: POST請求中,空Map無法轉成{} JSON字串 (#I455O2)
  • fix: 過濾器引數總是為第一個引數 (#I43VV0)
  • fix: 自定義請求頭content-type會替換為大寫 (#I46WNW)
  • fix: 在Spring專案中如果不設定轉換器就會找不到Converter (#I46FKV)
  • fix: Response不帶Content-Type和Content-Encoding頭時無法正常解析 (#I455PO)
  • fix: 當請求 302 請求時,Forest 會自動的存取重定向的url,導致 302 的響應頭拿不到 (#I4AF3B)
  • fix: SpringSSLKeyStore 在Spring中初始化失敗 fix: 設定有ForestConfiguration引數的轉換器的時候,在springboot中會初始化失敗 (#I4AKT3)
  • fix: 在多執行緒環境下使用上傳檔案介面,執行時間長後會報出堆疊溢位的錯誤 (#I37UGY)
  • fix: BeanPostProcessor 介面在低版本 springboot 環境下不相容

優化內容:

  • opt: 優化 StringUtils 工具類方法
  • opt: 優化 URLUtils 工具類方法

程式碼改動:

  • add: SpringForestProperties類
  • add: 在所有請求註解中(如 @Request, @Get)新增 responseEncoding 屬性,用於強制指定響應資料的編碼格式
  • add: SpringForestObjectFactory類 add: ForestResponse.isRedirection 方法
  • add: ForestResponse.getRedirectionLocation 方法
  • add: ForestResponse.redirectionRequest 方法
  • add: ForestHeaderMap.clone 方法
  • add: ForestQueryMap.clone 方法
  • refactor: retryCount屬性不在建議使用
  • update: 去掉MethodLifeCycle
  • refactor: 修改Forest介面掃描邏輯
  • refactor: 將 TypeReference 類改為抽象類

特別鳴謝:

感謝他們參與的貢獻,排名不分先後

  • @CHMing
  • @ifaxin
展開閱讀全文