SpringBoot3-WebClient配置與使用詳解
1. 簡介
WebClient是Spring 5引入的響應(yīng)式Web客戶端,用于執(zhí)行HTTP請求。相比傳統(tǒng)的RestTemplate,WebClient提供了非阻塞、響應(yīng)式的方式來處理HTTP請求,是Spring推薦的新一代HTTP客戶端工具。本文將詳細(xì)介紹如何在SpringBoot 3.x中配置和使用WebClient。
2. 環(huán)境準(zhǔn)備
2.1 依賴配置
在 pom.xml中添加必要的依賴:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>3. WebClient配置
3.1 基礎(chǔ)配置
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("https://echo.apifox.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
}
}3.2 高級配置
package com.coderjia.boot3webflux.config;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import java.time.Duration;
/**
* @author CoderJia
* @create 2024/12/3 下午 09:42
* @Description
**/
@Slf4j
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
// 配置HTTP連接池
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxConnections(500)
.maxIdleTime(Duration.ofSeconds(20))
.build();
// 配置HTTP客戶端
HttpClient httpClient = HttpClient.create(provider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.responseTimeout(Duration.ofSeconds(5))
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(5))
.addHandlerLast(new WriteTimeoutHandler(5)));
// 構(gòu)建WebClient實(shí)例
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.baseUrl("https://echo.apifox.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
// 添加請求日志記錄功能
.filter(ExchangeFilterFunction.ofRequestProcessor(
clientRequest -> {
log.debug("Request: {} {}",
clientRequest.method(),
clientRequest.url());
return Mono.just(clientRequest);
}
))
// 添加響應(yīng)日志記錄功能
.filter(ExchangeFilterFunction.ofResponseProcessor(
clientResponse -> {
log.debug("Response status: {}",
clientResponse.statusCode());
return Mono.just(clientResponse);
}
))
.build();
}
}3.3 retrieve()和exchange()區(qū)別
在使用 WebClient 進(jìn)行 HTTP 請求時(shí),retrieve() 和 exchange() 方法都可以用來處理響應(yīng),但它們有不同的用途和行為。以下是它們的主要區(qū)別:
retrieve()
- 用途:retrieve() 方法用于簡化響應(yīng)處理,特別是當(dāng)你只需要響應(yīng)體時(shí)。
- 自動(dòng)錯(cuò)誤處理:retrieve() 會自動(dòng)處理 HTTP 錯(cuò)誤狀態(tài)碼(例如 4xx 和 5xx),并拋出 WebClientResponseException 及其子類。
- 返回值:通常用于直接獲取響應(yīng)體,例如 bodyToMono(String.class) 或 bodyToFlux(String.class)。
- 適用場景:適用于大多數(shù)常見的請求處理場景,特別是當(dāng)你不需要手動(dòng)處理響應(yīng)狀態(tài)碼時(shí)。
exchange()
- 用途:exchange() 方法提供了更底層的控制,允許你手動(dòng)處理響應(yīng),包括響應(yīng)狀態(tài)碼和響應(yīng)頭。
- 手動(dòng)錯(cuò)誤處理:exchange() 不會自動(dòng)處理 HTTP 錯(cuò)誤狀態(tài)碼,你需要手動(dòng)檢查響應(yīng)狀態(tài)碼并進(jìn)行相應(yīng)的處理。
- 返回值:返回 ClientResponse 對象,你可以從中提取響應(yīng)狀態(tài)碼、響應(yīng)頭和響應(yīng)體。
- 適用場景:適用于需要手動(dòng)處理響應(yīng)狀態(tài)碼或響應(yīng)頭的復(fù)雜場景。
示例對比
retrieve()
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JSONObject.class);
}exchange()
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
return response.bodyToMono(JSONObject.class);
} else {
return Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode()));
}
});
}4. 使用示例
4.1 基本請求操作
package com.coderjia.boot3webflux.service;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* @author CoderJia
* @create 2024/12/3 下午 10:22
* @Description
**/
@Service
public class ApiService {
@Resource
private WebClient webClient;
// GET請求
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JSONObject.class);
}
// POST請求
public Mono<JSONObject> post(JSONObject body) {
return webClient.post()
.uri("/post")
.bodyValue(body)
.retrieve()
.bodyToMono(JSONObject.class);
}
// PUT請求
public Mono<JSONObject> put(String q1, JSONObject JSONObject) {
return webClient.put()
.uri(uriBuilder -> uriBuilder
.path("/put")
.queryParam("q1", q1)
.build())
.bodyValue(JSONObject)
.retrieve()
.bodyToMono(JSONObject.class);
}
// DELETE請求
public Mono<JSONObject> delete(String q1) {
return webClient.delete()
.uri(uriBuilder -> uriBuilder
.path("/delete")
.queryParam("q1", q1)
.build())
.retrieve()
.bodyToMono(JSONObject.class);
}
}效果展示




4.2 處理復(fù)雜響應(yīng)
@Service
public class ApiService {
// 獲取列表數(shù)據(jù)
public Flux<JSONObject> getAllUsers() {
return webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(JSONObject.class);
}
// 處理錯(cuò)誤響應(yīng)
public Mono<JSONObject> getUserWithErrorHandling(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("客戶端錯(cuò)誤")))
.onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("服務(wù)器錯(cuò)誤")))
.bodyToMono(JSONObject.class);
}
// 使用exchange()方法獲取完整響應(yīng)
public Mono<ResponseEntity<JSONObject>> getUserWithFullResponse(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> response.toEntity(JSONObject.class));
}
}4.3 高級用法
@Service
public class ApiService {
// 帶請求頭的請求
public Mono<JSONObject> getUserWithHeaders(Long id, String token) {
return webClient.get()
.uri("/users/{id}", id)
.header("Authorization", "Bearer " + token)
.retrieve()
.bodyToMono(JSONObject.class);
}
// 帶查詢參數(shù)的請求
public Flux<JSONObject> searchUsers(String name, int age) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/users/search")
.queryParam("name", name)
.queryParam("age", age)
.build())
.retrieve()
.bodyToFlux(JSONObject.class);
}
// 文件上傳
public Mono<String> uploadFile(FilePart filePart) {
return webClient.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData("file", filePart))
.retrieve()
.bodyToMono(String.class);
}
}5. 最佳實(shí)踐
合理使用響應(yīng)式類型
- 使用 Mono 用于單個(gè)對象
- 使用 Flux 用于集合數(shù)據(jù)
- 注意背壓處理
錯(cuò)誤處理
public Mono<JSONObject> getUserWithRetry(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(JSONObject.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)))
.timeout(Duration.ofSeconds(5))
.onErrorResume(TimeoutException.class,
e -> Mono.error(new RuntimeException("請求超時(shí)")));
}資源管理
- 使用連接池
- 設(shè)置適當(dāng)?shù)某瑫r(shí)時(shí)間
- 實(shí)現(xiàn)優(yōu)雅關(guān)閉
6. 注意事項(xiàng)
- WebClient 是非阻塞的,需要注意響應(yīng)式編程的特性
- 合理配置連接池和超時(shí)參數(shù)
- 在生產(chǎn)環(huán)境中實(shí)現(xiàn)適當(dāng)?shù)腻e(cuò)誤處理和重試機(jī)制
- 注意內(nèi)存使用,特別是處理大量數(shù)據(jù)時(shí)
7. 與RestTemplate對比
| 特性 | WebClient | RestTemplate |
|---|---|---|
| 編程模型 | 響應(yīng)式、非阻塞 | 同步、阻塞 |
| 性能 | 更好 | 一般 |
| 資源利用 | 更高效 | 一般 |
| 學(xué)習(xí)曲線 | 較陡 | 平緩 |
| 適用場景 | 高并發(fā)、響應(yīng)式系統(tǒng) | 簡單應(yīng)用、傳統(tǒng)系統(tǒng) |
8. 總結(jié)
WebClient 作為 Spring 推薦的新一代 HTTP 客戶端,提供了強(qiáng)大的響應(yīng)式編程能力和更好的性能。雖然相比 RestTemplate 有一定的學(xué)習(xí)曲線,但在現(xiàn)代微服務(wù)架構(gòu)中,其帶來的好處遠(yuǎn)超過學(xué)習(xí)成本。建議在新項(xiàng)目中優(yōu)先考慮使用WebClient,特別是在需要處理高并發(fā)請求的場景下。
參考資料
到此這篇關(guān)于SpringBoot3-WebClient配置與使用詳解的文章就介紹到這了,更多相關(guān)SpringBoot3 WebClient使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?retry實(shí)現(xiàn)方法請求重試的使用步驟
這篇文章主要介紹了spring?retry實(shí)現(xiàn)方法請求重試及使用步驟,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
springboot+jwt+微信小程序授權(quán)登錄獲取token的方法實(shí)例
本文主要介紹了springboot+jwt+微信小程序授權(quán)登錄獲取token的方法實(shí)例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Java常用面板之JScrollPane滾動(dòng)面板實(shí)例詳解
這篇文章主要介紹了Java常用面板JScrollPane的簡單介紹和一個(gè)相關(guān)實(shí)例,,需要的朋友可以參考下。2017-08-08
intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法
這篇文章主要介紹了intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
基于Java實(shí)現(xiàn)圖片相似度對比的示例代碼
很多時(shí)候我們需要將兩個(gè)圖片進(jìn)行對比,確定兩個(gè)圖片的相似度。本文將利用Java和OpenCV庫實(shí)現(xiàn)圖片相似度對比,感興趣的可以動(dòng)手嘗試一下2022-07-07

