ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot WebClient 从入门到精通:非阻塞HTTP客户端实战指南

Spring Boot WebClient 从入门到精通:非阻塞HTTP客户端实战指南 1. 项目概述为什么是WebClient在Spring生态里做HTTP调用RestTemplate这个名字大家肯定不陌生。从Spring 3.0时代起它就是处理同步HTTP请求的“老大哥”简单、直接用了很多年。但如果你最近几年还在新项目里用RestTemplate可能就得反思一下技术栈的更新速度了。Spring官方从5.0版本开始就明确推荐使用WebClient作为新的非阻塞、响应式HTTP客户端并且在最新的Spring 6和Spring Boot 3中RestTemplate甚至进入了维护模式未来可能会被移除。这不仅仅是“推荐”而是一个明确的技术演进方向。那么WebClient到底强在哪里简单说它生来就是为了应对现代高并发、低延迟的微服务场景。它基于Project Reactor这个响应式编程库构建这意味着它从底层就是非阻塞、异步的。想象一下你的服务需要同时调用下游五六个接口来聚合数据如果用传统的RestTemplate你会发起五个阻塞式的HTTP调用线程会傻傻地等待每个请求返回这期间宝贵的线程资源就被白白占用了并发一高线程池很容易被打满。而WebClient则不同它发起请求后线程立即释放可以去处理其他任务等响应回来时再由事件驱动机制触发后续处理逻辑。这种模式能让你用极少的线程甚至就几个支撑极高的并发连接资源利用率天差地别。除了性能WebClient的API设计也更现代、更函数式。它提供了一套流畅的FluentAPI从构建请求、设置头信息、提交数据到处理响应可以像写流水线一样串联起来代码非常清晰。同时它对各种数据格式JSON、XML、流数据的支持也更原生、更强大。所以无论你是从RestTemplate迁移过来还是在新项目中直接选用深入掌握WebClient都是Spring Boot开发者的一项必备技能。这篇文章我就结合自己趟过的坑和实战经验带你从入门到精通彻底搞懂WebClient。2. 核心依赖与环境准备要使用WebClient第一步自然是引入依赖。在Spring Boot项目中这非常简单。如果你用的是Spring Boot 2.x或3.x并且项目是基于Spring WebFlux响应式Web框架构建的那么spring-boot-starter-webfluxstarter已经包含了WebClient。2.1 依赖引入与版本选择对于大多数情况在pom.xml中添加以下依赖就足够了dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency这个starter会传递引入spring-webflux和reactor-core等核心库。这里有个关键点即使你的主应用不是响应式的比如还是传统的spring-boot-starter-webMVC应用你依然可以引入spring-boot-starter-webflux来使用WebClient。Spring Boot的自动配置很智能它会检测到你的使用场景并不会强制把你的应用变成完全的响应式应用。当然如果你整个项目都想转向响应式那直接用webfluxstarter替换掉webstarter即可。注意如果你只需要WebClient而不需要完整的WebFlux服务器功能理论上可以只引入spring-webflux但通过Spring Boot Starter来管理是最省心、版本最兼容的方式强烈推荐。对于Gradle项目在build.gradle中添加dependencies { implementation org.springframework.boot:spring-boot-starter-webflux }引入依赖后你就可以在项目的任何地方如Service层通过WebClient.create()来创建一个基础的客户端实例了。但通常我们不会这么简单地使用而是通过配置Bean来获得更强大、更可控的客户端。2.2 基础配置与Bean声明我强烈建议将WebClient配置为Spring容器管理的Bean。这样做的好处是1可以利用Spring的依赖注入2可以统一配置连接超时、读写超时、编解码器等全局属性3方便进行测试和替换。一个最基础的配置Bean如下import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClient; Configuration public class WebClientConfig { Bean public WebClient webClient() { return WebClient.builder() .baseUrl(https://api.example.com) // 设置基础URL后续请求可以省略主机部分 .defaultHeader(User-Agent, MySpringBootApp/1.0) // 设置默认请求头 .build(); } }然后在你的Service中就可以通过Autowired注入使用了Service public class MyApiService { private final WebClient webClient; public MyApiService(WebClient webClient) { this.webClient webClient; } public MonoString fetchData() { return webClient.get() .uri(/data) .retrieve() .bodyToMono(String.class); } }实操心得baseUrl的设置在微服务环境下特别有用。你可以为每个下游服务创建一个独立的WebClientBean并设置对应的baseUrl。这样在代码中只需要关心接口路径使代码更清晰也便于未来服务地址变更时的统一修改。3. WebClient核心API与使用模式详解WebClient的API设计围绕“Builder模式”和“函数式编程”展开核心操作可以概括为构建请求Build - 发送请求Exchange/Retrieve - 处理响应Handle Response。下面我们拆解每一步。3.1 构建请求GET, POST, PUT, DELETE创建请求的起点是调用webClient.method()其中method可以是get(),post(),put(),delete(),patch()等。GET请求是最简单的webClient.get() .uri(/users/{id}, 123) // 路径参数 .retrieve() // 发起请求并获取响应 .bodyToMono(User.class); // 将响应体转换为User对象.uri()方法非常灵活除了直接写路径还支持路径参数.uri(“/users/{id}”, userId)查询参数.uri(uriBuilder - uriBuilder.path(“/users”).queryParam(“name”, “zhangsan”).build())POST/PUT请求通常需要携带请求体webClient.post() .uri(/users) .contentType(MediaType.APPLICATION_JSON) // 设置Content-Type .bodyValue(new User(“张三”, “zhangsanexample.com”)) // 设置请求体对象会自动序列化为JSON .retrieve() .bodyToMono(User.class); // 假设创建后返回用户信息 // 或者使用BodyInserter更灵活可以处理Multipart、流数据等 webClient.post() .uri(/upload) .body(BodyInserters.fromMultipartData(formData)) .retrieve() .bodyToMono(String.class);设置请求头Headers除了在创建WebClient时设置默认头也可以在具体请求中覆盖或添加。webClient.get() .uri(/secure-data) .header(“Authorization”, “Bearer “ token) // 设置认证头 .accept(MediaType.APPLICATION_JSON) // 设置Accept头 .retrieve() .bodyToMono(Data.class);3.2 发送请求与处理响应retrieve() vs exchange()发送请求有两个核心方法retrieve()和exchange()。这是新手最容易混淆的地方。retrieve()方法是更高级的抽象也是最常用、最推荐的方式。它直接帮你处理了常见的HTTP状态码2xx状态码被认为是成功的会正常返回响应体4xx或5xx状态码会被包装成一个WebClientResponseException异常抛出。这符合大多数“成功则返回数据失败则抛异常”的编程直觉。MonoUser userMono webClient.get() .uri(“/users/1”) .retrieve() .bodyToMono(User.class); // 调用 subscribe() 或 block() 来实际触发请求测试时常用block生产环境慎用 User user userMono.block();exchange()方法则提供了更底层的控制。它返回一个MonoClientResponse让你能直接访问原始的响应对象包括状态码、响应头等。你需要手动检查状态码并决定如何处理。MonoUser userMono webClient.get() .uri(“/users/1”) .exchangeToMono(response - { if (response.statusCode().is2xxSuccessful()) { return response.bodyToMono(User.class); } else if (response.statusCode() HttpStatus.NOT_FOUND) { return Mono.empty(); // 返回空值而不是抛异常 } else { // 将错误响应转换为自定义异常 return response.createException() .flatMap(Mono::error); } });核心选择建议除非你有特殊需求需要根据非2xx状态码执行不同的业务逻辑例如404不视为错误而是返回空值否则一律使用retrieve()。它的代码更简洁错误处理更统一通过全局异常处理器或onError操作符能避免大量样板代码。3.3 响应体处理Mono与FluxWebClient的响应处理完全基于Reactor的Mono和Flux类型这是理解响应式编程的关键。Mono代表0或1个元素的异步序列。用于处理返回单个对象或空的响应比如bodyToMono(User.class)。Flux代表0到N个元素的异步序列。用于处理返回数组、列表或流式数据的响应比如bodyToFlux(Item.class)。将响应体转换为对象依赖于配置的HttpMessageReader默认情况下Jackson库负责JSON的编解码所以你直接传User.classWebClient就能自动把JSON响应体反序列化成Java对象。处理集合数据FluxItem itemsFlux webClient.get() .uri(“/items”) .retrieve() .bodyToFlux(Item.class); // 假设接口返回一个Item数组的JSON // 将Flux转换为List注意这会收集所有元素适合已知数据量不大的情况 MonoListItem itemListMono itemsFlux.collectList();处理原始数据有时你可能需要获取原始的响应体字符串或字节数组。MonoString stringBody webClient.get() .uri(“/text”) .retrieve() .bodyToMono(String.class); Monobyte[] byteArrayBody webClient.get() .uri(“/binary”) .retrieve() .bodyToMono(byte[].class);实操心得在处理Flux时尤其是从流式接口如Server-Sent Events消费数据时要特别注意背压Backpressure问题。bodyToFlux会尊重服务器的推送速度但如果处理不过来可以通过limitRate()等操作符进行控制避免内存溢出。4. 高级特性与生产级配置掌握了基础用法我们来看看那些让WebClient真正强大起来的高级特性和生产环境必备配置。4.1 超时与重试策略配置网络请求不稳定超时和重试是保障系统韧性的关键。WebClient的超时配置需要通过底层HttpClient默认使用Reactor Netty来实现。连接超时、响应超时配置import io.netty.channel.ChannelOption; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import reactor.netty.http.client.HttpClient; import java.time.Duration; Bean public WebClient customWebClient() { HttpClient httpClient HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) // 连接超时 5秒 .responseTimeout(Duration.ofSeconds(10)); // 响应超时 10秒 return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .baseUrl(“https://api.example.com”) .build(); }重试策略Reactor提供了强大的retryWhen操作符但结合WebClient使用更常见的是Retry组件。我们可以定义一个基于异常类型的重试逻辑。import reactor.util.retry.Retry; import java.time.Duration; public MonoString fetchWithRetry() { return webClient.get() .uri(“/unstable-api”) .retrieve() .bodyToMono(String.class) .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) // 最多重试3次指数退避1s, 2s, 4s .filter(throwable - throwable instanceof WebClientResponseException.TooManyRequests) // 只对429状态码重试 .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) - { // 重试耗尽后的处理可以抛出自定义异常 throw new ServiceException(“下游服务繁忙请稍后重试”); })); }注意事项重试并非万能要谨慎使用。对于POST等非幂等操作重试可能导致数据重复。通常只为GET请求或明确的幂等操作配置重试。重试的条件也要严格过滤例如只对网络超时IOException或特定的服务端错误如5xx、429进行重试而不是对所有异常都重试。4.2 请求与响应日志拦截在生产环境排查问题时能看到详细的请求和响应日志至关重要。我们可以通过自定义ExchangeFilterFunction来实现。import lombok.extern.slf4j.Slf4j; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import reactor.core.publisher.Mono; Slf4j public class WebClientLoggingFilter { public static ExchangeFilterFunction logRequest() { return ExchangeFilterFunction.ofRequestProcessor(clientRequest - { log.info(“Request: {} {}”, clientRequest.method(), clientRequest.url()); clientRequest.headers().forEach((name, values) - values.forEach(value - log.debug(“{}: {}”, name, value))); return Mono.just(clientRequest); }); } public static ExchangeFilterFunction logResponse() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse - { log.info(“Response Status: {}”, clientResponse.statusCode()); clientResponse.headers().asHttpHeaders().forEach((name, values) - values.forEach(value - log.debug(“{}: {}”, name, value))); return Mono.just(clientResponse); }); } }在构建WebClient时添加过滤器Bean public WebClient loggedWebClient() { return WebClient.builder() .baseUrl(“https://api.example.com”) .filter(WebClientLoggingFilter.logRequest()) .filter(WebClientLoggingFilter.logResponse()) .build(); }实操心得记录请求体/响应体需要小心因为它们可能很大或包含敏感信息如密码、令牌。在生产环境建议只在DEBUG级别记录Body或者对敏感字段进行脱敏处理后再记录。4.3 连接池与资源管理默认情况下Reactor Netty会为每个WebClient实例管理一个连接池。在高并发场景下合理配置连接池参数能极大提升性能。import reactor.netty.resources.ConnectionProvider; import java.time.Duration; Bean public WebClient pooledWebClient() { // 自定义连接提供者 ConnectionProvider provider ConnectionProvider.builder(“myConnectionPool”) .maxConnections(500) // 最大连接数 .maxIdleTime(Duration.ofSeconds(20)) // 最大空闲时间 .maxLifeTime(Duration.ofMinutes(5)) // 连接最大存活时间 .pendingAcquireTimeout(Duration.ofSeconds(60)) // 获取连接超时时间 .evictInBackground(Duration.ofSeconds(120)) // 后台清理间隔 .build(); HttpClient httpClient HttpClient.create(provider) .responseTimeout(Duration.ofSeconds(30)); return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .baseUrl(“https://api.example.com”) .build(); }关键参数解析maxConnections针对单个目标主机host:port的最大连接数。不是全局连接数。需要根据下游服务的承受能力和自身并发量来设定。maxIdleTime/maxLifeTime定期清理不活跃或老旧的连接防止内存泄漏和保持连接健康。pendingAcquireTimeout当所有连接都在使用时新的请求会排队等待。这个参数设置了等待获取连接的最长时间超时则抛出异常。这可以防止请求无限期等待。重要提示在Spring Boot应用中通常建议将WebClient声明为单例Bean而不是每次使用时创建。这样可以让连接池在整个应用生命周期内复用发挥最大效能。在应用关闭时Spring会负责清理连接池资源。4.4 处理SSL/TLS安全通道问题这是从热搜词里看到的一个高频问题“webclient 请求被中止: 未能创建 ssl/tls 安全通道。” 或者 “创建 tls 客户端 凭据时出现严重错误。内部错误状态为 10013。” 这类错误通常发生在Windows环境下或者与自签名证书、过时的协议/密码套件有关。场景一信任自签名证书或内部CA在开发测试环境下游服务可能使用自签名证书。默认的SSL上下文会验证证书导致失败。我们可以配置HttpClient跳过证书验证仅限测试环境。import io.netty.handler.ssl.SslContextBuilder; import reactor.netty.tcp.SslProvider; HttpClient httpClient HttpClient.create() .secure(spec - spec.sslContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) // 信任所有证书危险 .build())); // 更安全的方式将自签名证书导入到信任库然后指定信任库。 // KeyStore trustStore ... 加载包含证书的KeyStore // SslContext sslContext SslContextBuilder.forClient().trustManager(trustStore).build();场景二协商SSL/TLS协议版本或密码套件某些老旧服务器可能只支持老旧的TLS 1.0或1.1而客户端默认可能已禁用。或者反过来客户端环境如某些Windows Server缺少必要的加密算法导致无法创建安全上下文错误10013常与此相关。import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SupportedCipherSuiteFilter; import javax.net.ssl.SSLException; import java.util.Arrays; public HttpClient createHttpClient() throws SSLException { SslContext sslContext SslContextBuilder.forClient() // 明确指定协议版本谨慎使用低版本不安全 // .protocols(“TLSv1.2”, “TLSv1.3”) // 明确指定密码套件 // .ciphers(Arrays.asList(“TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256”)) .build(); return HttpClient.create() .secure(spec - spec.sslContext(sslContext)); }对于Windows上的错误10013它通常与系统缺少必要的密码套件或SSL配置有关。一个常见的临时解决方案同样是仅限测试是让JVM使用更兼容的SSL实现# 在JVM启动参数中添加 -Djdk.tls.client.protocolsTLSv1.2 -Dhttps.protocolsTLSv1.2 # 或者尝试使用IBM JSSE提供者如果可用 -Djavax.net.ssl.trustStoreTypeWindows-ROOT根本的解决方法是更新操作系统补丁或确保服务器支持现代、安全的TLS协议和密码套件。5. 实战场景与代码示例理论说再多不如看几个实际场景。下面我们通过几个典型用例把上面的知识点串联起来。5.1 场景一调用外部JSON API并解析假设我们需要调用一个公共天气API例如https://api.weather.com/v3获取数据。Service public class WeatherService { private final WebClient weatherWebClient; // 为特定服务创建专用WebClient Bean public WeatherService(Qualifier(“weatherWebClient”) WebClient weatherWebClient) { this.weatherWebClient weatherWebClient; } public MonoWeatherData getCurrentWeather(String city) { return weatherWebClient.get() .uri(uriBuilder - uriBuilder .path(“/current”) .queryParam(“city”, city) .queryParam(“units”, “metric”) .queryParam(“apikey”, “your-api-key-here”) // 密钥应从配置中心读取 .build()) .header(“Accept”, “application/json”) .retrieve() .onStatus(status - status.is4xxClientError(), response - { // 处理4xx错误例如API密钥无效 return Mono.error(new InvalidApiKeyException(“Weather API认证失败”)); }) .onStatus(status - status.is5xxServerError(), response - { // 处理5xx错误 return Mono.error(new ServiceUnavailableException(“天气服务暂时不可用”)); }) .bodyToMono(WeatherData.class) // 自动反序列化到WeatherData类 .timeout(Duration.ofSeconds(5)) // 为单个请求设置超时 .doOnSuccess(data - log.debug(“成功获取{}的天气数据”, city)) .doOnError(e - log.error(“获取{}天气数据失败”, city, e)); } } // 对应的配置类 Configuration public class WeatherApiConfig { Value(“${weather.api.base-url}”) private String weatherApiBaseUrl; Bean(“weatherWebClient”) public WebClient weatherWebClient() { return WebClient.builder() .baseUrl(weatherApiBaseUrl) .defaultHeader(“Accept”, “application/json”) .filter(logRequestResponse()) // 添加日志过滤器 .build(); } }5.2 场景二提交表单数据与文件上传调用需要application/x-www-form-urlencoded或multipart/form-data格式的接口。提交表单数据public MonoString login(String username, String password) { MultiValueMapString, String formData new LinkedMultiValueMap(); formData.add(“username”, username); formData.add(“password”, password); return webClient.post() .uri(“/login”) .contentType(MediaType.APPLICATION_FORM_URLENCODED) .bodyValue(formData) // 使用bodyValueWebClient会自动处理编码 .retrieve() .bodyToMono(String.class); // 假设返回一个token字符串 }上传文件public MonoString uploadFile(Path filePath, String description) { MultipartBodyBuilder builder new MultipartBodyBuilder(); builder.part(“file”, new FileSystemResource(filePath)) .header(“Content-Disposition”, “form-data; name\”file\”; filename\”” filePath.getFileName() “\””); builder.part(“description”, description); return webClient.post() .uri(“/upload”) .contentType(MediaType.MULTIPART_FORM_DATA) .body(BodyInserters.fromMultipartData(builder.build())) .retrieve() .bodyToMono(String.class); }5.3 场景三处理流式响应如SSE, Stream JSON有些API会返回流式数据例如服务器发送事件Server-Sent Events, SSE或流式JSON每行一个JSON对象。WebClient可以很好地处理这种场景。public FluxStockPrice getStockPriceStream(String symbol) { return webClient.get() .uri(“/stocks/{symbol}/stream”, symbol) .accept(MediaType.TEXT_EVENT_STREAM) // 接受SSE流 .retrieve() .bodyToFlux(String.class) // 先按行获取字符串 .filter(line - line.startsWith(“data: “)) // SSE格式为 “data: {...}” .map(line - line.substring(6)) // 去掉 “data: ” 前缀 .map(json - { try { return objectMapper.readValue(json, StockPrice.class); // 解析JSON } catch (JsonProcessingException e) { throw new RuntimeException(“解析股票数据失败”, e); } }) .doOnNext(price - log.info(“收到股票价格更新: {}”, price)) .doOnError(e - log.error(“股票数据流异常中断”, e)) .doOnComplete(() - log.info(“股票数据流正常结束”)); }在这个例子中我们订阅返回的FluxStockPrice它会持续不断地推送新的股票价格直到连接关闭。这种模式非常适合实时监控、数据看板等场景。6. 常见问题排查与性能调优即使掌握了所有API在实际生产中使用WebClient还是会遇到各种问题。下面我整理了一些典型问题和排查思路。6.1 请求未发送或响应未接收现象代码执行了但日志里没有请求发出或者没有收到响应。原因1没有订阅Subscribe。这是响应式编程新手最常犯的错误。WebClient的请求定义Mono或Flux是惰性的只有当你订阅它时请求才会真正发出。在测试代码里用block()是订阅在Controller里返回MonoSpring WebFlux框架会帮你订阅。但在普通的Service方法里如果你只是定义了Mono却没有返回它或被其他操作符订阅请求就不会发生。解决确保你的调用链最终被订阅。在业务方法中通常是将Mono/Flux作为返回值。如果需要在方法内部触发并处理可以使用subscribe()方法并妥善处理异常subscribe(value - {}, error - {})。现象请求超时日志显示ReadTimeoutException或ResponseTimeoutException。原因下游服务处理慢或者网络延迟高超过了配置的responseTimeout。排查检查下游服务本身的性能。使用curl或Postman直接调用下游接口确认响应时间。适当增加responseTimeout但更重要的是设置合理的重试和熔断策略避免慢请求拖垮整个系统。6.2 内存泄漏与连接未释放现象应用运行一段时间后内存持续增长或出现OutOfMemoryError。原因响应流Flux没有被正确消费或取消订阅导致数据积压在内存中。或者连接没有及时关闭。解决对于Flux流确保使用take(),limitRate(),timeout()等操作符来限制数据量或设置超时。对于无限流一定要有取消订阅的机制。资源清理WebClient底层使用的Reactor Netty连接池会在连接空闲超时后自动关闭。但如果你创建了大量一次性的WebClient实例而没有关闭可能会导致资源泄漏。始终坚持使用单例Bean。监控启用Netty的指标通过Micrometer监控连接池的活跃连接数、等待请求数等。6.3 并发限制与线程模型理解现象并发量上去后请求延迟急剧增加甚至超时失败。原因可能触及了连接池的maxConnections限制或者操作系统的文件描述符限制。调优调整连接池参数根据下游服务的性能和自身QPS合理设置maxConnections和pendingAcquireTimeout。一个经验公式是maxConnections ≈ (QPS * 平均响应时间(秒))并留有一定余量。理解线程模型WebClient是异步的它使用Netty的事件循环线程数量很少通常为CPU核心数来处理IO而不是传统的业务线程池。这意味着你的回调处理逻辑如map,flatMap中的代码不能有阻塞操作如Thread.sleep(), 同步锁阻塞的数据库调用否则会卡住事件循环线程导致所有请求都变慢。如果必须进行阻塞调用请使用publishOn或subscribeOn切换到专门的弹性线程池Schedulers.boundedElastic()。webClient.get() .uri(“/slow”) .retrieve() .bodyToMono(String.class) .publishOn(Schedulers.boundedElastic()) // 切换到弹性线程池执行后续阻塞操作 .map(response - { // 这里可以执行一些阻塞操作比如调用一个传统的阻塞式DAO return blockingRepository.save(response); });6.4 序列化与反序列化问题现象抛出JsonDecodeException或WebClientResponseException$InternalServerError提示JSON解析错误。原因1日期格式不匹配。API返回的日期字符串格式与Jackson默认格式或JsonFormat注解指定的格式不符。解决在自定义的ObjectMapperBean中配置默认日期格式或者在DTO字段上使用JsonFormat(pattern “yyyy-MM-dd’T’HH:mm:ss”)。原因2字段名不匹配蛇形vs驼峰。API返回user_name但你的Java字段是userName。解决在ObjectMapper中配置PropertyNamingStrategies.SNAKE_CASE或者在字段上使用JsonProperty(“user_name”)。原因3未知属性。API返回的JSON中有你的DTO里没有的字段。解决在类级别添加JsonIgnoreProperties(ignoreUnknown true)防止解析失败。为WebClient配置自定义解码器Bean public WebClient webClientWithCustomMapper(ObjectMapper objectMapper) { ExchangeStrategies strategies ExchangeStrategies.builder() .codecs(clientDefaultCodecsConfigurer - { clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder( new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON)); clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder( new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON)); }) .build(); return WebClient.builder() .exchangeStrategies(strategies) .baseUrl(“https://api.example.com”) .build(); }WebClient是一个强大而现代的HTTP客户端它的响应式特性需要开发者转变思维方式从命令式的“等待结果”转向声明式的“定义数据流”。一旦掌握它能给你的应用带来巨大的性能和资源利用率提升。从简单的GET请求到复杂的流处理、从基础配置到生产级调优希望这篇详解能成为你手边可靠的参考。在实际项目中多结合日志、监控和压力测试才能找到最适合你业务场景的配置和使用方式。
返回列表