1. 概述
我們的服務經常與其他的 REST 服務進行通信,以獲取信息。
從 Spring 5 開始,我們可以使用 <em >WebClient</em> 來執行這些請求,以一種反應式、非阻塞的方式。<em >WebClient</em> 是新 <em >WebFlux</em> 框架的一部分,該框架建立在 <em >Project Reactor</em> 之上。它具有流式、反應式的 API,並且在底層實現中使用了 HTTP 協議。
當我們發起一個 Web 請求時,數據通常以 JSON 格式返回。<em >WebClient</em> 可以代表我們進行轉換。
在本文中,我們將瞭解如何使用 <em >WebClient</em> 將 JSON 數組轉換為 Java 中的 <em >Array</em> (對象數組)、POJO 數組以及 POJO 列表。
2. 依賴項
要使用 WebClient,我們需要將幾個依賴項添加到我們的 pom.xml 中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>3. JSON、POJO 和服務
讓我們從一個端點 http://localhost:8080/readers 開始,它返回一個包含讀者及其最喜歡的書籍的 JSON 數組:
[{ "id": 1, "name": "reader1", "favouriteBook": { "author": "Milan Kundera", "title": "The Unbearable Lightness of Being" }}, { "id": 2, "name": "reader2" "favouriteBook": { "author": "Douglas Adams", "title": "The Hitchhiker's Guide to the Galaxy" } }]
我們需要相應的 Reader 和Book 類來處理數據:
public class Reader {
private int id;
private String name;
private Book favouriteBook;
// getters and setters..
}public class Book {
private final String author;
private final String title;
// getters and setters..
}對於我們的接口實現,我們使用 ReaderConsumerServiceImpl ,並使用 WebClient 作為其依賴項:
public class ReaderConsumerServiceImpl implements ReaderConsumerService {
private final WebClient webClient;
public ReaderConsumerServiceImpl(WebClient webclient) {
this.webclient = webclient;
}
// ...
}4. 將 JSON 對象列表映射到 Java 集合
當從 REST 請求接收 JSON 數組時,有多種方法將其轉換為 Java 集合。讓我們探討各種選項,並瞭解如何輕鬆處理返回的數據。我們將分析讀者最喜歡的書籍。
4.1. Mono vs. Flux
Project Reactor 引入了兩個Publisher 實現:Mono 和 Flux。
Flux<T> 在我們需要處理零個到多個,或者潛在無限的結果時非常有用。我們可以將一個 Twitter 動態源(動態源)作為例子。
當我們知道結果一次性返回——就像我們的用例一樣——我們可以使用 Mono<T>.
4.2. 使用 WebClient 與 Object 數組
首先,使用 WebClient 的 get 方法進行調用,並使用類型為 Object[] 的 Mono 來收集響應:
Mono<Object[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Object[].class).log();接下來,我們把內容提取到我們的 Object 數組中:
Object[] objects = response.block();實際的 對象 指的是一個任意結構,包含我們的數據。讓我們將其轉換為 Reader 對象數組。
為此,我們需要一個 ObjectMapper:
ObjectMapper mapper = new ObjectMapper();在這裏,我們將其聲明為內聯,儘管通常將其作為類的私有靜態常量成員進行聲明。
最後,我們準備提取讀者的最愛書籍並將其收集到一個列表中:
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, Reader.class))
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());當我們要求 Jackson 解序列化器產生 Object 作為目標類型時,它實際上會將 LinkedHashMap 對象序列化成一系列 JSON 對象。 使用 convertValue 進行後處理效率低下。 如果我們在序列化過程中提供我們期望的類型給 Jackson,就可以避免這種情況。
4.3. 使用 Reader 數組與 WebClient
我們可以為我們的 WebClient 提供 Reader[] 數組,而不是 Object[] 數組。
Mono<Reader[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Reader[].class).log();
Reader[] readers = response.block();
return Arrays.stream(readers)
.map(Reader:getFavouriteBook)
.collect(Collectors.toList());在這裏,我們可以觀察到我們不再需要 ObjectMapper.convertValue。但是,我們仍然需要進行額外的轉換,以便使用 Java 的 Stream API 以及為了使我們的代碼與 List 兼容。
4.4. 使用 WebClient 與 Reader List
如果希望 Jackson 生成 Reader 的 List 而不是數組,我們需要描述我們想要創建的 List。為此,我們向方法提供一個由匿名內部類產生的 ParameterizedTypeReference:
Mono<List<Reader>> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<Reader>>() {});
List<Reader> readers = response.block();
return readers.stream()
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());這讓我們能夠使用 列表進行操作。
讓我們更深入地瞭解 為什麼我們需要使用 參數化類型引用。
Spring的WebClient可以輕鬆地將JSON反序列化為 Reader.class,當運行時類型信息可用時。
然而,使用泛型時,如果嘗試使用 List<Reader>.class,則會發生泛型擦除。因此,Jackson將無法確定泛型的類型參數。
通過使用 參數化類型引用,我們可以解決這個問題。將其實例化為匿名內部類利用了泛型類子類的編譯時類型信息,這些信息不受類型擦除的影響,並且可以通過反射進行消費。
5. 結論
在本教程中,我們探討了三種使用 WebClient 的方式來處理 JSON 對象。我們還學習瞭如何指定數組的 Object 類型以及自定義類。
我們還學習瞭如何使用 ParameterizedTypeReference 來提供生成 List 的信息類型。