SpringBoot3-WebClient配置与使用详解
作者:CoderJia
WebClient是Spring 5引入的响应式Web客户端,用于执行HTTP请求,相比传统的RestTemplate,WebClient提供了非阻塞、响应式的方式来处理HTTP请求,是Spring推荐的新一代HTTP客户端工具,本文将详细介绍如何在SpringBoot 3.x中配置和使用WebClient,一起看看吧
1. 简介
WebClient是Spring 5引入的响应式Web客户端,用于执行HTTP请求。相比传统的RestTemplate,WebClient提供了非阻塞、响应式的方式来处理HTTP请求,是Spring推荐的新一代HTTP客户端工具。本文将详细介绍如何在SpringBoot 3.x中配置和使用WebClient。
2. 环境准备
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 基础配置
@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))); // 构建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); } )) // 添加响应日志记录功能 .filter(ExchangeFilterFunction.ofResponseProcessor( clientResponse -> { log.debug("Response status: {}", clientResponse.statusCode()); return Mono.just(clientResponse); } )) .build(); } }
3.3 retrieve()和exchange()区别
在使用 WebClient 进行 HTTP 请求时,retrieve() 和 exchange() 方法都可以用来处理响应,但它们有不同的用途和行为。以下是它们的主要区别:
retrieve()
- 用途:retrieve() 方法用于简化响应处理,特别是当你只需要响应体时。
- 自动错误处理:retrieve() 会自动处理 HTTP 错误状态码(例如 4xx 和 5xx),并抛出 WebClientResponseException 及其子类。
- 返回值:通常用于直接获取响应体,例如 bodyToMono(String.class) 或 bodyToFlux(String.class)。
- 适用场景:适用于大多数常见的请求处理场景,特别是当你不需要手动处理响应状态码时。
exchange()
- 用途:exchange() 方法提供了更底层的控制,允许你手动处理响应,包括响应状态码和响应头。
- 手动错误处理:exchange() 不会自动处理 HTTP 错误状态码,你需要手动检查响应状态码并进行相应的处理。
- 返回值:返回 ClientResponse 对象,你可以从中提取响应状态码、响应头和响应体。
- 适用场景:适用于需要手动处理响应状态码或响应头的复杂场景。
示例对比
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 处理复杂响应
@Service public class ApiService { // 获取列表数据 public Flux<JSONObject> getAllUsers() { return webClient.get() .uri("/users") .retrieve() .bodyToFlux(JSONObject.class); } // 处理错误响应 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("服务器错误"))) .bodyToMono(JSONObject.class); } // 使用exchange()方法获取完整响应 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); } // 带查询参数的请求 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. 最佳实践
合理使用响应式类型
- 使用 Mono 用于单个对象
- 使用 Flux 用于集合数据
- 注意背压处理
错误处理
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("请求超时"))); }
资源管理
- 使用连接池
- 设置适当的超时时间
- 实现优雅关闭
6. 注意事项
- WebClient 是非阻塞的,需要注意响应式编程的特性
- 合理配置连接池和超时参数
- 在生产环境中实现适当的错误处理和重试机制
- 注意内存使用,特别是处理大量数据时
7. 与RestTemplate对比
特性 | WebClient | RestTemplate |
---|---|---|
编程模型 | 响应式、非阻塞 | 同步、阻塞 |
性能 | 更好 | 一般 |
资源利用 | 更高效 | 一般 |
学习曲线 | 较陡 | 平缓 |
适用场景 | 高并发、响应式系统 | 简单应用、传统系统 |
8. 总结
WebClient 作为 Spring 推荐的新一代 HTTP 客户端,提供了强大的响应式编程能力和更好的性能。虽然相比 RestTemplate 有一定的学习曲线,但在现代微服务架构中,其带来的好处远超过学习成本。建议在新项目中优先考虑使用WebClient,特别是在需要处理高并发请求的场景下。
参考资料
到此这篇关于SpringBoot3-WebClient配置与使用详解的文章就介绍到这了,更多相关SpringBoot3 WebClient使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!