導讀:本文從AI時代背景下的軟體架構變化談起,引出其中一個必要的Http客戶端技術,進而分析當前Java領域主要的Http客戶端和Http框架,並重點闡述了其中優雅度滿分的Retrofit框架,以及相關的使用技巧。透過閱讀本文,你可以快速瞭解Java領域的三大Http客戶端是什麼?三大封裝框架是什麼?以及熟練掌握Retrofit框架的各種使用技巧。
前沿
在AI技術日新月異的今天,軟體架構正經歷著前所未有的變革,以適應更加複雜、高效且智慧的資料互動需求。作為這一變革中的重要一環,HTTP客戶端技術成爲了連線前後端、大模型與Java、微服務之間不可或缺的橋樑。在Java這一廣泛應用的程式語言中,HTTP客戶端的選擇與使用不僅關乎到系統的效能與穩定性,還直接影響到開發效率與程式碼的可維護性。因此對於開發者掌握好Http客戶端和框架勢必重要!
三大框架是什麼?
在Java生態中,三大主流的HTTP客戶端分別是:
Apache HttpClient:作為Apache軟體基金會下的一個專案,HttpClient以其豐富的功能、高度的可配置性和廣泛的社羣支援而聞名。它支援HTTP/1.1和HTTP/2協議,提供了強大的請求執行、響應處理以及連線管理功能。
OkHttp:由Square公司開發,OkHttp以其高效、易用和強大的錯誤處理能力而受到青睞。它支援同步和非同步請求,內建了連線池、GZIP壓縮和HTTP/2等特性,非常適合在Android和Java後端服務中使用。
HttpUrlConnection:隨著Java 11的釋出,Java平臺內建了一個全新的HTTP客戶端API。這個API旨在提供簡單、現代的HTTP客戶端功能,同時保持與Java平臺的緊密整合。它支援HTTP/2和WebSocket,並提供了非同步和流式API,使得處理HTTP請求變得更加靈活和高效。
這三大客戶端封裝實現了HTTP協議,但是在封裝的API方法上千差萬別,且都不怎麼好用,因此開源社羣又陸續的出現了三大HTTP框架:
Spring RestTemplate(儘管Spring 5引入了WebClient作為更現代的替代品,但RestTemplate在舊專案中仍廣泛使用):RestTemplate是Spring框架提供的一個同步客戶端,用於簡化與HTTP服務的通訊。它提供了豐富的模板方法,用於處理HTTP請求,並自動將響應體繫結到Java物件上。
Retrofit(注意:雖然Retrofit在Android開發中非常流行,但嚴格來說它更多被用於Android而非純Java後端環境。不過,其設計理念和優雅度值得借鑑):Retrofit是一個型別安全的HTTP客戶端,用於Android和Java,它基於OkHttp構建,透過註解的方式極大地簡化了HTTP請求的編寫。Retrofit將HTTP請求抽象為Java介面,開發者只需定義介面方法並新增註解,即可實現複雜的HTTP請求。
Feign:Feign是一個宣告式的Web服務客戶端,它使得編寫Web服務客戶端變得更加簡單。Feign透過建立介面和註解的方式來定義服務繫結,它內部封裝了Ribbon和Hystrix,提供了負載均衡和斷路器功能,非常適合在微服務架構中使用。
企業開發者往往都使用更為簡單的框架,而這3大框架都為主流的,但是他們各自有很明顯的區別:
RestTemplate:Spring把HttpClient、OkHttp、HttpUrlConnection的使用統一成一套API,只做了統一,並API使用上並沒有簡化太多
Retrofit:採用宣告式方式定義http請求,使得呼叫http請求變成了對方法進行呼叫,非常優雅的方式供開發者使用
Feign:同Retrofit一樣,採用宣告式的方式定義http請求,但多用於微服務之間通訊
在實踐開發中,如果開發的是微服務專案則可首選Feign,因為它還支援更多微服務所需要的功能。而如果在單體專案中則首選Retrofit,其優雅的使用方式能大大簡化介面的開發、管理**。同時Retrofit底層透過OkHttp進行實現,效能相比其它框架最好。**
Retrofit框架是什麼?
Retrofit基於OkHttp開發的、型別安全的Android或Java Http框架。再此我們選用Retrofit框架的重點理由是它基於OkHttp封裝,效能好,同時它融入了流行的宣告式(PS:只需要定義,不需要實現)開發思想,便於我們開發和學習。
Retrofit入門
在Retrofit中要實現一個Http請求總體來說共有以下2個步驟:
1、匯入依賴
2、宣告API
3、生效API
匯入依賴
<dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> <version>2.11.0</version> </dependency>
宣告API
我們可以先建立一個介面類:cn.itcast.star.graph.comfyui.client.api.ComfyuiApi,並按如下程式碼進行編寫:
package cn.itcast.star.graph.comfyui.client.api; import retrofit2.Call; import retrofit2.http.GET; import java.util.HashMap; public interface ComfyuiApi { /** * 獲取系統資訊 * @return */ @GET("/system_stats") Call<HashMap> getSystemStats(); }
使用Retrofit來宣告API,其實就是宣告介面方法,比如上述類中定義了一個方法getSystemStats:
方法名可以任意取,但建議與http介面名稱一致
方法透過@GET註解宣告當前請求方式是GET並且請求的地址為/system_stats,即獲取Comfyui的系統資訊介面
方法返回結果為Call<HashMap>物件
Call是固定的返回物件
HashMap則是介面返回的資料自動轉成Map集合儲存
生效API
上面宣告類要生效,我們還需要配置生效,可建立一個配置類:cn.itcast.star.graph.comfyui.client.config.ComfyuiConfig
package cn.itcast.star.graph.comfyui.client.config; import cn.itcast.star.graph.comfyui.client.api.ComfyuiApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; import java.io.IOException; @Configuration public class ComfyuiConfig { @Bean public ComfyuiApi comfyuiApi() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://192.168.100.129:8188") .addConverterFactory(JacksonConverterFactory.create()) .build(); ComfyuiApi comfyuiApi = retrofit.create(ComfyuiApi.class); return comfyuiApi; } }
在類中:
首先透過Retrofit.Builder構建一個Retrofit客戶端
透過baseUrl指定請求的伺服器地址
透過addConverterFactory指定請求資料的轉換器
最後呼叫 retrofit.create方法建立ComfyuiApi介面的實現
透過上述程式碼,在Spring IOC中就是宣告好了一個可以遠端呼叫獲取Comfyui伺服器狀態的Bean.
測試API
在測試包下建立類:cn.itcast.star.graph.ComfyuiApiTest
package cn.itcast.star.graph; import cn.itcast.star.graph.comfyui.client.api.ComfyuiApi; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class ComfyuiApiTest { @Autowired ComfyuiApi comfyuiApi; @Test public void test() throws Exception { System.out.println(comfyuiApi.getSystemStats().execute().body()); } }
在類中,直接注入ComfyuiApi,並呼叫getSystemStats(),即可發起請求並列印出結果:
Retrofit 註解大全
Retrofit爲了應對更復雜的Http請求,提供了豐富的註解來支援:
@Query:用來宣告http查詢部分的引數,與@RequestParam類似
@Path:用於說明請求路徑引數,與@PathVariable類似
@GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
@Body:用於宣告請求體物件,與@RequestBody類似
@POST("users/new") Call<User> createUser(@Body User user);
@Multipart:用於說明當前請求以表單形式發起,常常用於帶有檔案的介面
如果是檔案欄位需要使用 MultipartBody.Part
如果是非檔案欄位需要使用 RequestBody
@Part:用於說明一個表單欄位
@Multipart @PUT("user/photo") Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
@Headers:設定請求頭資訊
@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App" }) @GET("users/{username}") Call<User> getUser(@Path("username") String username);
@Header:設定一個頭欄位
@GET("user") Call<User> getUser(@Header("Authorization") String authorization)
@HeaderMap :把一個Map內容設定到請求頭中
@GET("user") Call<User> getUser(@HeaderMap Map<String, String> headers)
Retrofit 高階配置
日誌開啟
Retrofit還提供了列印日誌的功能,方便我們進行BUG的排錯。Retrofit日誌功能預設是關閉的,可以透過以下程式碼進行開啟:
@Bean public ComfyuiApi comfyuiApi() throws IOException { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .retryOnConnectionFailure(true) .connectTimeout(30, TimeUnit.SECONDS) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://192.168.100.129:8188") .client(okHttpClient) .addConverterFactory(JacksonConverterFactory.create()) .build(); ComfyuiApi comfyuiApi = retrofit.create(ComfyuiApi.class); return comfyuiApi; }
我們之前提供過Retrofit框架底層是透過OkHttp實現,Retrofit的日誌功能也是交由底層OkHttp實現:
由於要重新設定OkHttp,因此程式碼中自行構建了OkHttpClient
給OkHttpClient增加了日誌攔截器HttpLoggingInterceptor,並設定攔截器的日誌級別為BODY
NONE:不輸出
BASIC:輸出基本摘要
HEADERS:輸出頭資訊
BODY:輸出body資料
最後第14行透過client方法,重新設定Retrofit底層使用的OkHttp客戶端
按上述程式碼進行修改後,重新執行ComfyuiApiTest類,即可在控制檯看見相關的日誌輸出:
修改資料轉換器
Retrofit支援把Http的資料直接轉換成各種物件、各種資料格式,要使用那種格式,可透過配置資料轉換器自行選擇,目前Retrofit支援8種:
Gson:
com.squareup.retrofit2:converter-gson
Jackson:
com.squareup.retrofit2:converter-jackson
Moshi:
com.squareup.retrofit2:converter-moshi
Protobuf:
com.squareup.retrofit2:converter-protobuf
Wire:
com.squareup.retrofit2:converter-wire
Simple XML:
com.squareup.retrofit2:converter-simplexml
JAXB:
com.squareup.retrofit2:converter-jaxb
Scalars (primitives, boxed, and String):
com.squareup.retrofit2:converter-scalars
在專案中如果要使用GSON,則先需要匯入對應的依賴:
<dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>converter-gson</artifactId> <version>2.11.0</version> </dependency>
然後在程式碼中透過addConverterFactory方法進行,設定轉換工廠類:
工廠類命令為XXXConverterFactory,如果是Jackson則為JacksonConverterFactory,以此類推。
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); GitHubService service = retrofit.create(GitHubService.class);