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實例 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 請求時,retrieve() 和 exchange() 方法都可以用來處理響應(yīng),但它們有不同的用途和行為。以下是它們的主要區(qū)別:
retrieve()
- 用途:retrieve() 方法用于簡化響應(yīng)處理,特別是當(dāng)你只需要響應(yīng)體時。
- 自動錯誤處理:retrieve() 會自動處理 HTTP 錯誤狀態(tài)碼(例如 4xx 和 5xx),并拋出 WebClientResponseException 及其子類。
- 返回值:通常用于直接獲取響應(yīng)體,例如 bodyToMono(String.class) 或 bodyToFlux(String.class)。
- 適用場景:適用于大多數(shù)常見的請求處理場景,特別是當(dāng)你不需要手動處理響應(yīng)狀態(tài)碼時。
exchange()
- 用途:exchange() 方法提供了更底層的控制,允許你手動處理響應(yīng),包括響應(yīng)狀態(tài)碼和響應(yīng)頭。
- 手動錯誤處理:exchange() 不會自動處理 HTTP 錯誤狀態(tài)碼,你需要手動檢查響應(yīng)狀態(tài)碼并進(jìn)行相應(yīng)的處理。
- 返回值:返回 ClientResponse 對象,你可以從中提取響應(yīng)狀態(tài)碼、響應(yīng)頭和響應(yī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); } // 處理錯誤響應(yīng) public Mono<JSONObject> getUserWithErrorHandling(Long id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("客戶端錯誤"))) .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("服務(wù)器錯誤"))) .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. 最佳實踐
合理使用響應(yīng)式類型
- 使用 Mono 用于單個對象
- 使用 Flux 用于集合數(shù)據(jù)
- 注意背壓處理
錯誤處理
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è)置適當(dāng)?shù)某瑫r時間
- 實現(xiàn)優(yōu)雅關(guān)閉
6. 注意事項
- WebClient 是非阻塞的,需要注意響應(yīng)式編程的特性
- 合理配置連接池和超時參數(shù)
- 在生產(chǎn)環(huán)境中實現(xiàn)適當(dāng)?shù)腻e誤處理和重試機制
- 注意內(nèi)存使用,特別是處理大量數(shù)據(jù)時
7. 與RestTemplate對比
特性 | WebClient | RestTemplate |
---|---|---|
編程模型 | 響應(yīng)式、非阻塞 | 同步、阻塞 |
性能 | 更好 | 一般 |
資源利用 | 更高效 | 一般 |
學(xué)習(xí)曲線 | 較陡 | 平緩 |
適用場景 | 高并發(fā)、響應(yīng)式系統(tǒng) | 簡單應(yīng)用、傳統(tǒng)系統(tǒng) |
8. 總結(jié)
WebClient 作為 Spring 推薦的新一代 HTTP 客戶端,提供了強大的響應(yīng)式編程能力和更好的性能。雖然相比 RestTemplate 有一定的學(xué)習(xí)曲線,但在現(xiàn)代微服務(wù)架構(gòu)中,其帶來的好處遠(yuǎn)超過學(xué)習(xí)成本。建議在新項目中優(yōu)先考慮使用WebClient,特別是在需要處理高并發(fā)請求的場景下。
參考資料
到此這篇關(guān)于SpringBoot3-WebClient配置與使用詳解的文章就介紹到這了,更多相關(guān)SpringBoot3 WebClient使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot @Async 異步任務(wù)執(zhí)行方法
本篇文章主要介紹了Spring Boot @Async 異步任務(wù)執(zhí)行方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05SSH框架網(wǎng)上商城項目第22戰(zhàn)之銀行圖標(biāo)以及支付頁面顯示
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項目第22戰(zhàn)之銀行圖標(biāo)以及支付頁面顯示,感興趣的小伙伴們可以參考一下2016-06-06關(guān)于Java Interface接口的簡單練習(xí)題
這篇文章主要給大家分享的是關(guān)于Java Interface接口的簡單練習(xí)題,難度不算大,但是要有一個清晰的邏輯建立接口和鏈接Java類。下面來看看文章的詳細(xì)介紹吧,需要的朋友可以參考一下2021-11-11