1. 程式人生 > 其它 >retrofit get請求_【學習充電】Spring Boot專案整合Retrofit最佳實踐,最優雅的HTTP客戶端工具!...

retrofit get請求_【學習充電】Spring Boot專案整合Retrofit最佳實踐,最優雅的HTTP客戶端工具!...

技術標籤:retrofit get請求

57170a146a3275169f83a9d2296df8f3.gif

大家都知道 OKHttp 是一款由 Square公司開源的 Java 版本 HTTP 客戶端工具。實際上,Square公司還開源了基於OKHttp進一步封裝的 Retrofit工具,用來支援通過介面的方式發起HTTP請求。如果你的專案中還在直接使用 RestTemplate 或者OKHttp,或者基於它們封裝的 HttpUtils,那麼你可以嘗試使用 Retrofit。

retrofit-spring-boot-starter 實現了 Retrofit 與 spring-boot 框架快速整合,並且支援了部分功能增強,從而極大的簡化 spring-boot 專案下HTTP介面呼叫開發。接下來我們直接通過 retrofit-spring-boot-starter,來看看 spring-boot 專案傳送HTTP請求有多簡單。

Retrofit官方並沒有提供與 spring-boot 快速整合的 starter。retrofit-spring-boot-starter 是筆者封裝的,已在生產環境使用,非常穩定。造輪子不易,跪求各位大佬 star。

引入依賴

com.github.lianjiatechretrofit-spring-boot-starter2.0.2

配置 @RetrofitScan 註解

你可以給帶有@Configuration 的類配置 @RetrofitScan,或者直接配置到 spring-boot 的啟動類上。程式碼如下:

@SpringBootApplication@RetrofitScan("com.github.lianjiatech.retrofit.spring.boot.test")
publicclassRetrofitTestApplication{public static void main(String[] args) { SpringApplication.run(RetrofitTestApplication.class, args); }}

定義http介面

介面必須使用 @RetrofitClient 註解標記!

@RetrofitClient(baseUrl = "${test.baseUrl}")publicinterfaceHttpApi{@GET("person")    Result getPerson(@Query("id") Long id);
}

注入使用

將介面注入到其它 Service 中即可使用。

@ServicepublicclassTestService{@AutowiredprivateHttpApihttpApi;public void test() {// 通過 httpApi 發起 HTTP 請求    }}

只要通過上述幾個步驟,就能實現通過介面傳送 HTTP 請求了,真的很簡單。如果你在 spring-boot 專案裡面使用過 MyBatis,相信你對這種使用方式會更加熟悉。接下來我們繼續介紹一下 retrofit-spring-boot-starter 更高階一點的功能。

註解式攔截器

很多時候,我們希望某個介面下的某些HTTP請求執行統一的攔截處理邏輯。這個時候可以使用註解式攔截器。使用的步驟主要分為2步:

  1. 繼承 BasePathMatchInterceptor 編寫攔截處理器;
  2. 介面上使用 @Intercept 進行標註。

下面以給指定請求的 URL 後面拼接 timestamp 時間戳為例,介紹下如何使用註解式攔截器。

繼承 BasePathMatchInterceptor 編寫攔截處理器

@ComponentpublicclassTimeStampInterceptorextendsBasePathMatchInterceptor{@Overridepublic Response doIntercept(Chain chain) throws IOException {        Request request = chain.request();        HttpUrl url = request.url();long timestamp = System.currentTimeMillis();        HttpUrl newUrl = url.newBuilder()                .addQueryParameter("timestamp", String.valueOf(timestamp))                .build();        Request newRequest = request.newBuilder()                .url(newUrl)                .build();return chain.proceed(newRequest);    }}

介面上使用 @Intercept 進行標註

@RetrofitClient(baseUrl = "${test.baseUrl}")@Intercept(handler = TimeStampInterceptor.class, include = {"/api/**"}, exclude = "/api/test/savePerson")publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);@POST("savePerson")    Result savePerson(@Body Person person);}

上面的 @Intercept 配置表示:攔截 HttpApi 介面下/api/**路徑下(排除 /api/test/savePerson)的請求,攔截處理器使用 TimeStampInterceptor。

擴充套件註解式攔截器

有的時候,我們需要在攔截註解動態傳入一些引數,然後再執行攔截的時候需要使用這個引數。這種時候,我們可以擴充套件實現自定義攔截註解。自定義攔截註解必須使用 @InterceptMark 標記,並且註解中必須包括 include()、exclude()、handler() 屬性資訊。使用的步驟主要分為3步:

  1. 自定義攔截註解;
  2. 繼承 BasePathMatchInterceptor 編寫攔截處理器;
  3. 介面上使用自定義攔截註解。

例如我們需要在請求頭裡面動態加入 accessKeyId、accessKeySecret 簽名信息才能正常發起 http 請求,這個時候可以自定義一個加簽攔截器註解 @Sign 來實現。下面以自定義 @Sign 攔截註解為例進行說明。

自定義@Sign註解

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@InterceptMarkpublic @interface Sign {/**     * 金鑰 key     * 支援佔位符形式配置。     *     * @return     */String accessKeyId();/**     * 金鑰     * 支援佔位符形式配置。     *     * @return     */String accessKeySecret();/**     * 攔截器匹配路徑     *     * @return     */String[] include() default {"/**"};/**     * 攔截器排除匹配,排除指定路徑攔截     *     * @return     */String[] exclude() default {};/**     * 處理該註解的攔截器類*優先從 Spring 容器獲取對應的 Bean,如果獲取不到,則使用反射建立一個!     *     * @return     */    Class extends BasePathMatchInterceptor> handler() default SignInterceptor.class;}

擴充套件自定義攔截註解有以下2點需要注意:

  1. 自定義攔截註解必須使用 @InterceptMark 標記;
  2. 註解中必須包括 include()、exclude()、handler() 屬性資訊。

實現 SignInterceptor

@ComponentpublicclassSignInterceptorextendsBasePathMatchInterceptor{privateStringaccessKeyId;private String accessKeySecret;public void setAccessKeyId(String accessKeyId) {this.accessKeyId = accessKeyId;    }public void setAccessKeySecret(String accessKeySecret) {this.accessKeySecret = accessKeySecret;    }@Overridepublic Response doIntercept(Chain chain) throws IOException {        Request request = chain.request();        Request newReq = request.newBuilder()                .addHeader("accessKeyId", accessKeyId)                .addHeader("accessKeySecret", accessKeySecret)                .build();return chain.proceed(newReq);    }}

上述accessKeyId和accessKeySecret欄位值會依據@Sign註解的accessKeyId()和 accessKeySecret()值自動注入。如果@Sign指定的是佔位符形式的字串,則會取配置屬性值進行注入。另外,accessKeyId 和 accessKeySecret 欄位必須提供 setter 方法。

介面上使用 @Sign

@RetrofitClient(baseUrl = "${test.baseUrl}")@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", exclude = {"/api/test/person"})publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);@POST("savePerson")    Result savePerson(@Body Person person);}

連線池管理

預設情況下,所有通過 Retrofit 傳送的 HTTP 請求都會使用 max-idle-connections=5 keep-alive-second=300 的預設連線池。當然,我們也可以在配置檔案中配置多個自定義的連線池,然後通過 @RetrofitClient 的 poolName 屬性來指定使用。比如我們要讓某個介面下的請求全部使用 poolName=test1 的連線池,程式碼實現如下:

1. 配置連線池。
retrofit:    # 連線池配置pool:test1:max-idle-connections: 3keep-alive-second: 100test2:max-idle-connections: 5keep-alive-second:50
2. 通過 @RetrofitClient 的 poolName 屬性來指定使用的連線池。
@RetrofitClient(baseUrl = "${test.baseUrl}", poolName="test1")publicinterfaceHttpApi{@GET("person")    Result getPerson(@Query("id") Long id);}

日誌列印

很多情況下,我們希望將 HTTP 請求日誌記錄下來。通過 @RetrofitClient 的 logLevel 和 logStrategy 屬性,您可以指定每個介面的日誌列印級別以及日誌列印策略。

retrofit-spring-boot-starter 支援了5種日誌列印級別(ERROR、WARN、INFO、DEBUG、TRACE),預設 INFO。支援了4種日誌列印策略(NONE、BASIC、HEADERS、BODY),預設 BASIC。4種日誌列印策略含義如下:
  1. NONE:無日誌;
  2. BASIC:Request 與 Response 日誌;
  3. HEADERS:Request 與 Response 日誌,帶 HEADER;
  4. BODY:Request 與 Response 日誌,帶 HEADER 與 BODY(可選)。

retrofit-spring-boot-starter 預設使用了 DefaultLoggingInterceptor 執行真正的日誌列印功能,其底層就是 OKHttp 原生的 HttpLoggingInterceptor。當然,你也可以自定義實現自己的日誌列印攔截器,只需要繼承 BaseLoggingInterceptor(具體可以參考 DefaultLoggingInterceptor 的實現),然後在配置檔案中進行相關配置即可。

retrofit:  # 日誌列印攔截器logging-interceptor:com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor

HTTP 異常資訊格式化器

當出現 HTTP 請求異常時,原始的異常資訊可能閱讀起來並不友好,因此 retrofit-spring-boot-starter 提供了HTTP異常資訊格式化器,用來美化輸出HTTP請求引數,預設使用 DefaultHttpExceptionMessageFormatter 進行請求資料格式化。你也可以進行自定義,只需要繼承 BaseHttpExceptionMessageFormatter,再進行相關配置即可。

retrofit:  # Http異常資訊格式化器http-exception-message-formatter:com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter

呼叫介面卡 CallAdapter

Retrofit 可以通過呼叫介面卡 CallAdapterFactory 將 Call 物件適配成介面方法的返回值型別。retrofit-spring-boot-starter 擴充套件兩種 CallAdapterFactory 實現:

  1. BodyCallAdapterFactory
  • 預設啟用,可通過配置 retrofit.enable-body-call-adapter=false 關閉;
  • 同步執行HTTP請求,將響應體內容適配成介面方法的返回值型別例項;
  • 除了 Retrofit.Call、Retrofit.Response、java.util.concurrent.CompletableFuture 之外,其它返回型別都可以使用該介面卡。
ResponseCallAdapterFactory
  • 預設啟用,可通過配置 retrofit.enable-response-call-adapter=false 關閉;
  • 同步執行HTTP請求,將響應體內容適配成 Retrofit.Response 返回;
  • 如果方法的返回值型別為 Retrofit.Response,則可以使用該介面卡。

Retrofit 自動根據方法返回值型別選用對應的CallAdapterFactory執行適配處理!加上Retrofit 預設的CallAdapterFactory,可支援多種形式的方法返回值型別:

  • Call: 不執行適配處理,直接返回 Call 物件;
  • CompletableFuture: 將響應體內容適配成 CompletableFuture 物件返回
  • Void: 不關注返回型別可以使用Void。如果HTTP狀態碼不是2xx,直接報錯;
  • Response: 將響應內容適配成 Response 物件返回;
  • 其他任意 Java 型別:將響應體內容適配成一個對應的 Java 型別物件返回,如果HTTP狀態碼不是2xx,直接報錯。
/** * Call * 不執行適配處理,直接返回 Call 物件 * @param id * @return */@GET("person")Call> getPersonCall(@Query("id") Long id);/** *  CompletableFuture *  將響應體內容適配成 CompletableFuture 物件返回 * @param id * @return */@GET("person")CompletableFuture> getPersonCompletableFuture(@Query("id") Long id);/** * Void * 不關注返回型別可以使用 Void。如果http狀態碼不是2xx,直接拋錯! * @param id * @return */@GET("person")Void getPersonVoid(@Query("id") Long id);/** *  Response *  將響應內容適配成Response物件返回 * @param id * @return */@GET("person")Response> getPersonResponse(@Query("id") Long id);/** * 其他任意Java型別 * 將響應體內容適配成一個對應的Java型別物件返回,如果http狀態碼不是2xx,直接拋錯! * @param id * @return */@GET("person")ResultgetPerson(@Query("id")Longid);

我們也可以通過繼承 CallAdapter.Factory 擴充套件實現自己的 CallAdapter;然後將自定義的 CallAdapterFactory 配置成 Spring 的 Bean!

自定義配置的 CallAdapter.Factory 優先順序更高!

資料轉碼器 Converter

Retrofit 使用 Converter 將 @Body 註解標註的物件轉換成請求體,將響應體資料轉換成一個 Java 物件,可以選用以下幾種 Converter:

  • Gson:com.squareup.Retrofit:converter-gson
  • Jackson:com.squareup.Retrofit:converter-jackson
  • Moshi:com.squareup.Retrofit:converter-moshi
  • Protobuf:com.squareup.Retrofit:converter-protobuf
  • Wire:com.squareup.Retrofit:converter-wire
  • Simple XML:com.squareup.Retrofit:converter-simplexml

retrofit-spring-boot-starter 預設使用的是 Jackson 進行序列化轉換!如果需要使用其它序列化方式,在專案中引入對應的依賴,再把對應的 ConverterFactory 配置成 Spring 的 Bean 即可。

我們也可以通過繼承 Converter.Factory 擴充套件實現自己的 Converter;然後將自定義的 Converter.Factory 配置成 Spring 的 Bean!
自定義配置的 Converter.Factory 優先順序更高!

全域性攔截器 BaseGlobalInterceptor

如果我們需要對整個系統的的 HTTP 請求執行統一的攔截處理,可以自定義實現全域性攔截器 BaseGlobalInterceptor, 並配置成 Spring 中的 Bean!例如我們需要在整個系統發起的HTTP請求,都帶上來源資訊。

@Componentpublic class SourceInterceptor extends BaseGlobalInterceptor {@Overridepublic Response doIntercept(Chain chain) throws IOException {        Request request = chain.request();        Request newReq = request.newBuilder()                .addHeader("source", "test")                .build();return chain.proceed(newReq);    }}

結語

至此,spring-boot 專案下最優雅的HTTP客戶端工具介紹就結束了,更多詳細資訊可以參考官方文件:Retrofit以及retrofit-spring-boot-starter。實現原理解讀可檢視基於 Retrofit 實現自己的輕量級HTTP呼叫工具。

979a86f23aa7e6f63eed4f467e532e5a.gif 19e89cc720689fe4e1bf831948f1fa75.gif 6b55313ecc92b5e09e1dda103216f5a1.png公眾號ID:柏維資訊 19e89cc720689fe4e1bf831948f1fa75.gif