SpringBoot WebClient 介绍 目录一、什么是 WebClient二、 WebClient 能解决什么问题三、WebClient 和 RestTemplate 的区别四、WebClient 的核心优势1. 非阻塞Non-Blocking2. 支持异步3. 链式 API 更现代五、WebClient 的核心对象六、Mono 和 Flux 是什么七、如何引入 WebClientMaven八、WebClient 的基本创建方式1. 创建 WebClient 最简单的使用方式2.Spring Bean 配置类方式(推荐)九、GET 带参数请求代码代码解析get()uri()headers()retrieve()bodyToMono()block()十、GET 返回对象User 类调用十一、POST 请求示例请求DTOPOST 代码bodyValue() 是什么十二、PUT 请求十三、DELETE 请求十四、下载文件下载文件为 byte[]保存本地文件大文件下载推荐流式十五、错误处理普通写法onStatus 状态码错误处理try-catch十六、retrieve() 和 exchangeToMono() 区别retrieve()最常用exchangeToMono()十七、 WebClient适合场景与不适合场景十八、WebClient 学习路线十九、最常用写法总结1.GET2.POST3.设置 header4.设置 body5.获取响应6.阻塞等待二十、完整实战示例二十一、最后总结一、什么是 WebClientWebClient是 Spring 5 引入的一个现代 HTTP 客户端属于 Spring WebFlux 模块用来发送 HTTP 请求GET、POST、PUT、DELETE 等。它可以替代传统的RestTemplate。(Home)WebClient 它底层基于ReactorNettyNIO因此少量线程 处理大量请求这也是它高并发能力强的原因。(Home)官方文档Spring WebClient 官方文档Spring WebFlux 官方文档二、 WebClient 能解决什么问题它主要用于调用第三方接口微服务之间通信下载文件上传文件调用 AI / OpenAPI 接口高并发 HTTP 请求异步并发调用多个接口例如你的系统 ↓ WebClient ↓ 支付宝接口 / 微信接口 / 第三方系统三、WebClient 和 RestTemplate 的区别对比项WebClientRestTemplate所属Spring WebFluxSpring MVC是否异步支持默认同步阻塞是否非阻塞是否是否支持响应式支持 Mono / Flux不支持并发能力高一般是否支持流式处理支持一般推荐程度新项目推荐维护模式API 风格链式 fluent API模板式 APISpring 官方已经说明RestTemplate进入 maintenance mode维护模式新项目更推荐WebClient(Reddit)四、WebClient 的核心优势1. 非阻塞Non-Blocking传统 RestTemplate线程发请求 ↓ 一直等待响应 ↓ 线程被占用WebClient线程发请求 ↓ 不用等待 ↓ 线程去处理别的任务 ↓ 响应回来再通知因此更省线程更适合高并发更适合微服务支持同步调用WebClient 虽然是响应式的但你也能.block()变成同步调用。因此即使你不是响应式项目也能使用 WebClient。2. 支持异步可以同时请求多个接口MonoUseruserMonowebClient.get()...MonoOrderorderMonowebClient.get()...最后组合Mono.zip(userMono,orderMono)3. 链式 API 更现代传统的 RestTemplaterestTemplate.exchange(...)而 WebClientwebClient.get().uri(/user).retrieve().bodyToMono(User.class);更像Java8 Stream函数式编程Reactor 响应式风格五、WebClient 的核心对象最重要的WebClient它类似浏览器客户端负责发请求设置 header设置 token接收响应下载文件六、Mono 和 Flux 是什么WebClient 基于 Reactor。两个核心类类型含义Mono0~1 个结果Flux0~N 个结果例如MonoUserFluxUser表示MonoUser表示未来会返回一个 User FluxUser表示未来会返回多个 User七、如何引入 WebClientMavenSpring Boot 项目dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webflux/artifactId/dependency即使你项目不是 WebFlux 项目也能单独使用 WebClient。八、WebClient 的基本创建方式1. 创建 WebClient 最简单的使用方式WebClientwebClientWebClient.create();或者WebClientwebClientWebClient.create(https://api.example.com);或者WebClientwebClientWebClient.builder().baseUrl(https://api.example.com).build();2.Spring Bean 配置类方式(推荐)配置类ConfigurationpublicclassWebClientConfig{BeanpublicWebClientwebClient(){//配置超时和日志HttpClienthttpClientHttpClient.create().responseTimeout(Duration.ofSeconds(10)).wiretap(true);returnWebClient.builder()//基本url域名.baseUrl(https://api.example.com)//默认请求头增加类型为json.defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE)//默认请求头增加token.defaultHeader(HttpHeaders.AUTHORIZATION,Bearer abcdefg)//超时配置和日志.clientConnector(newReactorClientHttpConnector(httpClient)).build();}}通常 token 是动态获取的可以使用 filter下面的示例使用 getToken() 动态获取 token。Filter 示例BeanpublicWebClientwebClient(){returnWebClient.builder().filter((request,next)-{ClientRequestnewRequestClientRequest.from(request).header(HttpHeaders.AUTHORIZATION,Bearer getToken()).build();returnnext.exchange(newRequest);}).build();}官方 builder 配置项包括baseUrldefaultHeaderfiltercodectimeoutconnector 等 (Spring 框架)注入使用AutowiredprivateWebClientwebClient;九、GET 带参数请求GET /user?page1size10代码StringresultwebClient.get().uri(uriBuilder-uriBuilder.path(/user).queryParam(page,1).queryParam(size,10).build()).headers(headers-{headers.setBearerAuth(token);headers.add(appId,1001);}).retrieve().bodyToMono(String.class).block();最终请求https://api.example.com/user?page1size10代码解析get()表示 GET 请求webClient.get()uri()请求地址。如果请求地址很简单可以这样写.uri(/user)headers()增加请求头可以在配置类里配置默认的请求头retrieve()开始发送请求并获取响应.retrieve()bodyToMono()响应转对象.bodyToMono(String.class)block()阻塞等待结果.block();注意WebClient 本身是异步的。调用.block()才会变成同步等待。十、GET 返回对象User 类DatapublicclassUser{privateLongid;privateStringname;}调用UseruserwebClient.get().uri(/user).retrieve().bodyToMono(User.class).block();如果带参数和请求头UseruserwebClient.get().uri(uriBuilder-uriBuilder.path(/user).queryParam(page,1).queryParam(size,10).build()).headers(headers-{headers.setBearerAuth(token);headers.add(appId,1001);}).retrieve().bodyToMono(User.class).block();通过 bodyToMono(User.class) Spring 会自动 JSON 转 User 对象。十一、POST 请求示例请求POST /user Content-Type: application/json请求体{name:张三,password:123,}DTODatapublicclassUserReq{privateStringname;privateStringpassword;}POST 代码UserReqreqnewUserReq();req.setName(张三);req.setPassword(123);StringtokenBearer xxxxxx;StringresultwebClient.post().uri(/user)//JSON 请求.contentType(MediaType.APPLICATION_JSON)//单次请求携带 Token. 可在配置类全局配置 Token.header(HttpHeaders.AUTHORIZATION,token).bodyValue(req).retrieve().bodyToMono(String.class).block();如果是表单请求.contentType(MediaType.APPLICATION_FORM_URLENCODED)bodyValue() 是什么它表示把对象转为请求体 JSON等价于{name:张三}十二、PUT 请求webClient.put().uri(/user/1).bodyValue(req).retrieve().bodyToMono(String.class).block();十三、DELETE 请求webClient.delete().uri(/user/1).retrieve().bodyToMono(String.class).block();十四、下载文件这是企业里非常常见的场景。下载文件为 byte[]byte[]datawebClient.get().uri(https://example.com/test.pdf)//如果需要携带token.header(HttpHeaders.AUTHORIZATION,Bearer xxxxxx).retrieve().bodyToMono(byte[].class).block();保存本地文件byte[]datawebClient.get().uri(https://example.com/test.pdf)//如果需要携带token.header(HttpHeaders.AUTHORIZATION,Bearer xxxxxx).retrieve().bodyToMono(byte[].class).block();Files.write(Paths.get(D:/test.pdf),data);大文件下载推荐流式如果文件很大 有几百 MB 或者 几 GB不推荐上面的 byte[] 下载否则可能 OOM内存溢出流式下载FluxDataBufferfluxwebClient.get().uri(/download/file).retrieve().bodyToFlux(DataBuffer.class);DataBufferUtils.write(flux,Paths.get(D:/big.zip),StandardOpenOption.CREATE).block();或者webClient.get().uri(https://example.com/big.zip).retrieve().bodyToFlux(DataBuffer.class).map(DataBuffer::asByteBuffer).doOnNext(buffer-{// 写入文件}).blockLast();十五、错误处理普通写法webClient.get().uri(/user).retrieve().bodyToMono(String.class)如果404 500会抛异常。onStatus 状态码错误处理StringresultwebClient.get().uri(/user).retrieve().onStatus(HttpStatusCode::is4xxClientError,response-Mono.error(newRuntimeException(4xx异常))).onStatus(HttpStatusCode::is5xxServerError,response-Mono.error(newRuntimeException(5xx异常))).bodyToMono(String.class).block();try-catchtry{StringresultwebClient.get().uri(/test).retrieve().bodyToMono(String.class).block();}catch(Exceptione){e.printStackTrace();}十六、retrieve() 和 exchangeToMono() 区别retrieve()最常用适合普通接口调用简洁开发简单场景.retrieve().bodyToMono(...)exchangeToMono()适合高级场景获取状态码获取响应头、cookie自定义响应处理StringresultwebClient.get().uri(/user).exchangeToMono(response-{if(response.statusCode().is2xxSuccessful()){returnresponse.bodyToMono(String.class);}returnMono.error(newRuntimeException(请求失败));}).block();十七、 WebClient适合场景与不适合场景非常适合微服务高并发API 网关聚合接口AI 调用并发请求多个服务SSE/流式响应不适合的场景如果你的项目完全同步 低并发 传统 MVC那么RestTemplate / RestClient可能更简单。社区里也有很多开发者提到WebFlux 会增加复杂度Mono / Flux 学习成本较高 (Reddit)十八、WebClient 学习路线建议按这个顺序学习WebClient 基础 APIMono / FluxReactor异步编程响应式编程Netty背压BackPressure十九、最常用写法总结1.GETwebClient.get()2.POSTwebClient.post()3.设置 header.header()4.设置 body.bodyValue()5.获取响应.retrieve().bodyToMono()6.阻塞等待.block()二十、完整实战示例封装 HttpClientServiceServicepublicclassHttpClientService{privatefinalWebClientwebClient;publicHttpClientService(WebClient.Builderbuilder){this.webClientbuilder.baseUrl(https://api.example.com).defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE).build();}// GETpublicStringgetUser(){returnwebClient.get().uri(/user/1).retrieve().bodyToMono(String.class).block();}// POSTpublicStringlogin(LoginRequestrequest){returnwebClient.post().uri(/login).bodyValue(request).retrieve().bodyToMono(String.class).block();}// token请求publicStringgetWithToken(Stringtoken){returnwebClient.get().uri(/user/info).header(HttpHeaders.AUTHORIZATION,Bearer token).retrieve().bodyToMono(String.class).block();}}二十一、最后总结WebClient 本质上Spring 官方现代 HTTP 客户端它最大的特点非阻塞响应式高并发异步链式 API企业中现在越来越多微服务 WebClient的组合。但它的核心难点其实不是 WebClient 本身而是Mono / Flux / Reactor