Spring中網(wǎng)絡請求客戶端WebClient的使用詳解
在 Spring 5 之前,如果我們想要調用其他系統(tǒng)提供的 HTTP 服務,通??梢允褂?Spring 提供的 RestTemplate 來訪問,不過由于 RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客戶端,因此存在一定性能瓶頸。根據(jù) Spring 官方文檔介紹,在將來的版本中它可能會被棄用。
? 作為替代,Spring 官方已在 Spring 5 中引入了 WebClient 作為非阻塞式 Reactive HTTP 客戶端。下面通過樣例演示如何使用 WebClient。
一、基本介紹
1.什么是 WebClient
從 Spring 5 開始,Spring 中全面引入了 Reactive 響應式編程。而 WebClient 則是 Spring WebFlux 模塊提供的一個非阻塞的基于響應式編程的進行 Http 請求的客戶端工具。
由于 WebClient 的請求模式屬于異步非阻塞,能夠以少量固定的線程處理高并發(fā)的 HTTP 請求。因此,從 Spring 5 開始,HTTP 服務之間的通信我們就可以考慮使用 WebClient 來取代之前的 RestTemplate。
2.WebClient 的優(yōu)勢
(1)與 RestTemplate 相比,WebClient 有如下優(yōu)勢:
- 非阻塞,Reactive 的,并支持更高的并發(fā)性和更少的硬件資源。
- 提供利用 Java 8 lambdas 的函數(shù) API。
- 支持同步和異步方案。
- 支持從服務器向上或向下流式傳輸。
(2)RestTemplate 不適合在非阻塞應用程序中使用,因此 Spring WebFlux 應用程序應始終使用 WebClient。在大多數(shù)高并發(fā)場景中,WebClient 也應該是 Spring MVC 中的首選,并且用于編寫一系列遠程,相互依賴的調用。
3.安裝配置
編輯 pom.xml 文件,添加 Spring WebFlux 依賴,從而可以使用 WebClient。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
二、創(chuàng)建 WebClient 實例
? 從 WebClient 的源碼中可以看出,WebClient 接口提供了三個不同的靜態(tài)方法來創(chuàng)建 WebClient 實例:
1.利用 create() 創(chuàng)建
(1)下面利用 create() 方法創(chuàng)建一個 WebClient 對象,并利用該對象請求一個網(wǎng)絡接口,最后將結果以字符串的形式打印出來。
注意:由于利用 create() 創(chuàng)建的 WebClient 對象沒有設定 baseURL,所以這里的 uri() 方法相當于重寫 baseURL。
WebClient webClient = WebClient.create();
Mono<String> mono = webClient
.get() // GET 請求
.uri("http://jsonplaceholder.typicode.com/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
2.利用 create(String baseUrl) 創(chuàng)建
(1)下面利用 create(String baseUrl) 方法創(chuàng)建一個 WebClient 對象,并利用該對象請求一個網(wǎng)絡接口,最后將結果以字符串的形式打印出來。
注意:由于利用 create(String baseUrl) 創(chuàng)建的 WebClient 對象時已經(jīng)設定了 baseURL,所以 uri() 方法會將返回的結果和 baseUrl 進行拼接組成最終需要遠程請求的資源 URL。
WebClient webClient = WebClient.create("http://jsonplaceholder.typicode.com");
Mono<String> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
3.利用 builder 創(chuàng)建(推薦)
(1)下面使用 builder() 返回一個 WebClient.Builder,然后再調用 build 就可以返回 WebClient 對象。并利用該對象請求一個網(wǎng)絡接口,最后將結果以字符串的形式打印出來。
注意:由于返回的不是 WebClient 類型而是 WebClient.Builder,我們可以通過返回的 WebClient.Builder 設置一些配置參數(shù)(例如:baseUrl、header、cookie 等),然后再調用 build 就可以返回 WebClient 對象了
WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.defaultHeader(HttpHeaders.USER_AGENT,"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")
.defaultCookie("ACCESS_TOKEN", "test_token")
.build();
Mono<String> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
三、GET 請求
1.獲取 String 結果數(shù)據(jù)
下面代碼將響應結果映射為一個 String 字符串,并打印出來。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
Mono<String> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
return;
}
}2.將結果轉換為對象
(1)當響應的結果是 JSON 時,也可以直接指定為一個 Object,WebClient 將接收到響應后把 JSON 字符串轉換為對應的對象。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
Mono<PostBean> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(PostBean.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
return;
}
}(2)其中定義的實體 Bean 代碼如下:
@Getter
@Setter
@ToString
public class PostBean {
private int userId;
private int id;
private String title;
private String body;
}
3.將結果轉成集合
(1)假設接口返回的是一個 json 數(shù)組,內容如下:

(2)我們也可以將其轉成對應的 Bean 集合:
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
Flux<PostBean> flux = webClient
.get() // GET 請求
.uri("/posts") // 請求路徑
.retrieve() // 獲取響應體
.bodyToFlux(PostBean.class); //響應數(shù)據(jù)類型轉換
List<PostBean> posts = flux.collectList().block();
System.out.println("結果數(shù):" + posts.size());
return;
}
}4.參數(shù)傳遞的幾種方式
下面 3 種方式的結果都是一樣的。
(1)使用占位符的形式傳遞參數(shù):
Mono<String> mono = webClient
.get() // GET 請求
.uri("/{1}/{2}", "posts", "1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
(2)另一種使用占位符的形式:
String type = "posts";
int id = 1;
Mono<String> mono = webClient
.get() // GET 請求
.uri("/{type}/{id}", type, id) // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
System.out.println(mono.block());
(3)我們也可以使用 map 裝載參數(shù):
Map<String,Object> map = new HashMap<>();
map.put("type", "posts");
map.put("id", 1);
Mono<String> mono = webClient
.get() // GET 請求
.uri("/{type}/{id}", map) // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
5.subscribe 訂閱(非阻塞式調用)
(1)前面的樣例我們都是人為地使用 block 方法來阻塞當前程序。其實 WebClient 是異步的,也就是說等待響應的同時不會阻塞正在執(zhí)行的線程。只有在響應結果準備就緒時,才會發(fā)起通知。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
System.out.println("--- begin ---");
Mono<String> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
// 訂閱(異步處理結果)
mono.subscribe(result -> {
System.out.println(result);
});
System.out.println("--- end ---");
return;
}
}附:使用 exchange() 方法獲取完整的響應內容
1.方法介紹
(1)前面我們都是使用 retrieve() 方法直接獲取到了響應的內容,如果我們想獲取到響應的頭信息、Cookie 等,可以在通過 WebClient 請求時把調用 retrieve() 改為調用 exchange()。
(2)通過 exchange() 方法可以訪問到代表響應結果的對象,通過該對象我們可以獲取響應碼、contentType、contentLength、響應消息體等。
2.使用樣例
下面代碼請求一個網(wǎng)絡接口,并將響應體、響應頭、響應碼打印出來。其中響應體的類型設置為 String。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
Mono<ClientResponse> mono = webClient
.get() // GET 請求
.uri("/posts/1") // 請求路徑
.exchange();
// 獲取完整的響應對象
ClientResponse response = mono.block();
HttpStatus statusCode = response.statusCode(); // 獲取響應碼
int statusCodeValue = response.rawStatusCode(); // 獲取響應碼值
Headers headers = response.headers(); // 獲取響應頭
// 獲取響應體
Mono<String> resultMono = response.bodyToMono(String.class);
String body = resultMono.block();
// 輸出結果
System.out.println("statusCode:" + statusCode);
System.out.println("statusCodeValue:" + statusCodeValue);
System.out.println("headers:" + headers.asHttpHeaders());
System.out.println("body:" + body);
return;
}
}四、POST 請求
1.發(fā)送一個 JSON 格式數(shù)據(jù)(使用 json 字符串)
(1)下面代碼使用 post 方式發(fā)送一個 json 格式的字符串,并將結果打印出來(以字符串的形式)。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
// 需要提交的 json 字符串
String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";
// 發(fā)送請求
Mono<String> mono = webClient
.post() // POST 請求
.uri("/posts") // 請求路徑
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(jsonStr))
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
// 輸出結果
System.out.println(mono.block());
return;
}
}2.發(fā)送一個 JSON 格式數(shù)據(jù)(使用 Java Bean)
(1)下面代碼使用 post 方式發(fā)送一個 Bean 對象,并將結果打印出來(以字符串的形式)。結果同上面是一樣的:
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
// 要發(fā)送的數(shù)據(jù)對象
PostBean postBean = new PostBean();
postBean.setUserId(222);
postBean.setTitle("abc");
postBean.setBody("航歌");
// 發(fā)送請求
Mono<String> mono = webClient
.post() // POST 請求
.uri("/posts") // 請求路徑
.contentType(MediaType.APPLICATION_JSON_UTF8)
.syncBody(postBean)
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
// 輸出結果
System.out.println(mono.block());
return;
}
}(2)上面發(fā)送的 Bean 對象實際上會轉成如下格式的 JSON 數(shù)據(jù)提交:
3.使用 Form 表單的形式提交數(shù)據(jù)
(1)下面樣例使用 POST 方式發(fā)送 multipart/form-data 格式的數(shù)據(jù):
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
//提交參數(shù)設置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("title", "abc");
map.add("body", "航歌");
// 發(fā)送請求
Mono<String> mono = webClient
.post() // POST 請求
.uri("/posts") // 請求路徑
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(map))
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
// 輸出結果
System.out.println(mono.block());
return;
}
}(2)上面代碼最終會通過如下這種 form 表單方式提交數(shù)據(jù):
4.將結果轉成自定義對象
? 上面樣例我們都是將響應結果以 String 形式接收,其實 WebClient 還可以自動將響應結果轉成自定的對象或則數(shù)組。具體可以參考前面寫的文章:
5.設置 url 參數(shù)
(1)如果 url 地址上面需要傳遞一些參數(shù),可以使用占位符的方式:
String url = "http://jsonplaceholder.typicode.com/{1}/{2}";
String url = "http://jsonplaceholder.typicode.com/{type}/{id}";
(2)具體的用法可以參考前面寫的文章:
6.subscribe 訂閱(非阻塞式調用)
(1)前面的樣例我們都是人為地使用 block 方法來阻塞當前程序。其實 WebClient 是異步的,也就是說等待響應的同時不會阻塞正在執(zhí)行的線程。只有在響應結果準備就緒時,才會發(fā)起通知。
@RestController
public class HelloController {
// 創(chuàng)建 WebClient 對象
private WebClient webClient = WebClient.builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.build();
@GetMapping("/test")
public void test() {
System.out.println("--- begin ---");
// 需要提交的 json 字符串
String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";
Mono<String> mono = webClient
.post() // POST 請求
.uri("/posts") // 請求路徑
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(jsonStr))
.retrieve() // 獲取響應體
.bodyToMono(String.class); //響應數(shù)據(jù)類型轉換
// 訂閱(異步處理結果)
mono.subscribe(result -> {
System.out.println(result);
});
System.out.println("--- end ---");
return;
}
}以上就是Spring中網(wǎng)絡請求客戶端WebClient的使用詳解的詳細內容,更多關于Spring WebClient的資料請關注腳本之家其它相關文章!
相關文章
SpringMVC開發(fā)中十大常見問題深度解析與解決方案
在Java Web開發(fā)領域,SpringMVC作為一款主流的Web框架,憑借其強大的功能和便捷的開發(fā)體驗深受開發(fā)者喜愛,然而,在實際使用過程中,開發(fā)者常常會遇到各種各樣的“坑”,本文將針對SpringMVC開發(fā)中常見的十大問題,需要的朋友可以參考下2025-06-06
MyBatis-Plus動態(tài)表名使用selectPage方法不生效問題解析與解決方案
MyBatis-Plus是MyBatis的增強工具,動態(tài)表名是MyBatis-Plus的一個重要功能之一,一些開發(fā)者在使用selectPage方法時可能會遇到動態(tài)表名不生效的問題,本文將深入分析這個問題的原因,并提供相應的解決方案,需要的朋友可以參考下2023-12-12

