SpringBoot WebClient 介绍

目录

  • [一、什么是 WebClient?](#一、什么是 WebClient?)
  • [二、 WebClient 能解决什么问题?](#二、 WebClient 能解决什么问题?)
  • [三、WebClient 和 RestTemplate 的区别](#三、WebClient 和 RestTemplate 的区别)
  • [四、WebClient 的核心优势](#四、WebClient 的核心优势)
    • [1. 非阻塞(Non-Blocking)](#1. 非阻塞(Non-Blocking))
    • [2. 支持异步](#2. 支持异步)
    • [3. 链式 API 更现代](#3. 链式 API 更现代)
  • [五、WebClient 的核心对象](#五、WebClient 的核心对象)
  • [六、Mono 和 Flux 是什么?](#六、Mono 和 Flux 是什么?)
  • [七、如何引入 WebClient?](#七、如何引入 WebClient?)
  • [八、WebClient 的基本创建方式](#八、WebClient 的基本创建方式)
    • [1. 创建 WebClient 最简单的使用方式](#1. 创建 WebClient 最简单的使用方式)
    • [2.Spring Bean 配置类方式(推荐)](#2.Spring Bean 配置类方式(推荐))
  • [九、GET 带参数](#九、GET 带参数)
  • [十、GET 返回对象](#十、GET 返回对象)
    • [User 类](#User 类)
    • 调用
  • [十一、POST 请求示例](#十一、POST 请求示例)
    • 请求
    • DTO
    • [POST 代码](#POST 代码)
    • [bodyValue() 是什么?](#bodyValue() 是什么?)
  • [十二、PUT 请求](#十二、PUT 请求)
  • [十三、DELETE 请求](#十三、DELETE 请求)
  • 十四、下载文件
  • 十五、错误处理
  • [十六、retrieve() 和 exchangeToMono() 区别](#十六、retrieve() 和 exchangeToMono() 区别)
  • [十七、 WebClient适合场景与不适合场景?](#十七、 WebClient适合场景与不适合场景?)
  • [十八、WebClient 学习路线](#十八、WebClient 学习路线)
  • 十九、最常用写法总结
  • 二十、完整实战示例
  • 二十一、最后总结

一、什么是 WebClient?

WebClient 是 Spring 5 引入的一个现代 HTTP 客户端,属于 Spring WebFlux 模块,用来发送 HTTP 请求(GET、POST、PUT、DELETE 等)。

它可以替代传统的 RestTemplate。(Home)

WebClient 它底层基于:

  • Reactor
  • Netty
  • NIO

因此:

text 复制代码
少量线程
处理大量请求

这也是它高并发能力强的原因。(Home)

官方文档:


二、 WebClient 能解决什么问题?

它主要用于:

  • 调用第三方接口
  • 微服务之间通信
  • 下载文件
  • 上传文件
  • 调用 AI / OpenAPI 接口
  • 高并发 HTTP 请求
  • 异步并发调用多个接口

例如:

text 复制代码
你的系统
   ↓
WebClient
   ↓
支付宝接口 / 微信接口 / 第三方系统

三、WebClient 和 RestTemplate 的区别

对比项 WebClient RestTemplate
所属 Spring WebFlux Spring MVC
是否异步 支持 默认同步阻塞
是否非阻塞
是否支持响应式 支持 Mono / Flux 不支持
并发能力 一般
是否支持流式处理 支持 一般
推荐程度 新项目推荐 维护模式
API 风格 链式 fluent API 模板式 API

Spring 官方已经说明:

  • RestTemplate 进入 maintenance mode(维护模式)
  • 新项目更推荐 WebClient (Reddit)

四、WebClient 的核心优势

1. 非阻塞(Non-Blocking)

传统 RestTemplate:

text 复制代码
线程发请求
↓
一直等待响应
↓
线程被占用

WebClient:

text 复制代码
线程发请求
↓
不用等待
↓
线程去处理别的任务
↓
响应回来再通知

因此:

  • 更省线程
  • 更适合高并发
  • 更适合微服务

支持同步调用

WebClient 虽然是响应式的,但你也能:

java 复制代码
.block()

变成同步调用。

因此:

即使你不是响应式项目,也能使用 WebClient。


2. 支持异步

可以同时请求多个接口:

java 复制代码
Mono<User> userMono = webClient.get()...
Mono<Order> orderMono = webClient.get()...

最后组合:

java 复制代码
Mono.zip(userMono, orderMono)

3. 链式 API 更现代

传统的 RestTemplate:

java 复制代码
restTemplate.exchange(...)

而 WebClient:

java 复制代码
webClient.get()
         .uri("/user")
         .retrieve()
         .bodyToMono(User.class);

更像:

  • Java8 Stream
  • 函数式编程
  • Reactor 响应式风格

五、WebClient 的核心对象

最重要的:

java 复制代码
WebClient

它类似:

text 复制代码
浏览器客户端

负责:

  • 发请求
  • 设置 header
  • 设置 token
  • 接收响应
  • 下载文件

六、Mono 和 Flux 是什么?

WebClient 基于 Reactor。

两个核心类:

类型 含义
Mono 0~1 个结果
Flux 0~N 个结果

例如:

java 复制代码
Mono<User>
Flux<User>

表示:

text 复制代码
Mono<User>表示未来会返回一个 User
Flux<User>表示未来会返回多个 User

七、如何引入 WebClient?

Maven

Spring Boot 项目:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

即使你项目不是 WebFlux 项目,也能单独使用 WebClient。


八、WebClient 的基本创建方式

1. 创建 WebClient 最简单的使用方式

java 复制代码
WebClient webClient = WebClient.create();

或者:

java 复制代码
WebClient webClient = WebClient.create("https://api.example.com");

或者:

java 复制代码
WebClient webClient = WebClient.builder()
        .baseUrl("https://api.example.com")
        .build();

2.Spring Bean 配置类方式(推荐)

配置类

java 复制代码
@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient() {
        //配置超时和日志
        HttpClient httpClient = HttpClient.create()
            .responseTimeout(Duration.ofSeconds(10))
            .wiretap(true);
        
        return WebClient.builder()
                //基本url域名
                .baseUrl("https://api.example.com")
            	//默认请求头增加类型为json
                .defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE)
                //默认请求头增加token
        		.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer abcdefg")
            	//超时配置和日志
            	.clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
    }
}

通常 token 是动态获取的,可以使用 filter,下面的示例使用 getToken() 动态获取 token。

Filter 示例

java 复制代码
@Bean
public WebClient webClient() {

    return WebClient.builder()
            .filter((request, next) -> {
                ClientRequest newRequest =
                        ClientRequest.from(request)
                                .header(HttpHeaders.AUTHORIZATION,"Bearer " + getToken())
                                .build();
                return next.exchange(newRequest);})
            .build();
}

官方 builder 配置项包括:

  • baseUrl
  • defaultHeader
  • filter
  • codec
  • timeout
  • connector 等 (Spring 框架)

注入使用

java 复制代码
@Autowired
private WebClient webClient;

九、GET 带参数

请求

http 复制代码
GET /user?page=1&size=10

代码

java 复制代码
String result = webClient.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();

最终请求:

java 复制代码
https://api.example.com/user?page=1&size=10

代码解析

get()

表示 GET 请求:

java 复制代码
webClient.get()

uri()

请求地址。

如果请求地址很简单可以这样写:

java 复制代码
.uri("/user")

headers()

增加请求头,可以在配置类里配置默认的请求头


retrieve()

开始发送请求并获取响应:

java 复制代码
.retrieve()

bodyToMono()

响应转对象:

java 复制代码
.bodyToMono(String.class)

block()

阻塞等待结果:

java 复制代码
.block();

注意:

WebClient 本身是异步的。

调用:

java 复制代码
.block()

才会变成同步等待。


十、GET 返回对象

User 类

java 复制代码
@Data
public class User {

    private Long id;

    private String name;
}

调用

java 复制代码
User user = webClient.get()
        .uri("/user")
        .retrieve()
        .bodyToMono(User.class)
        .block();

如果带参数和请求头:

java 复制代码
User user = webClient.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 请求示例

请求

http 复制代码
POST /user
Content-Type: application/json

请求体:

json 复制代码
{
  "name":"张三",
  "password":"123",
}

DTO

java 复制代码
@Data
public class UserReq {
    private String name;
    private String password;
}

POST 代码

java 复制代码
UserReq req = new UserReq();
req.setName("张三");
req.setPassword("123");

String token = "Bearer xxxxxx";

String result = webClient.post()
        .uri("/user")
    	//JSON 请求
    	.contentType(MediaType.APPLICATION_JSON)
   		 //单次请求携带 Token. 可在配置类全局配置 Token
    	.header(HttpHeaders.AUTHORIZATION, token)
        .bodyValue(req)
        .retrieve()
        .bodyToMono(String.class)
        .block();

如果是表单请求:

java 复制代码
.contentType(MediaType.APPLICATION_FORM_URLENCODED)

bodyValue() 是什么?

它表示:

text 复制代码
把对象转为请求体 JSON

等价于:

json 复制代码
{
  "name":"张三"
}

十二、PUT 请求

java 复制代码
webClient.put()
        .uri("/user/1")
        .bodyValue(req)
        .retrieve()
        .bodyToMono(String.class)
        .block();

十三、DELETE 请求

java 复制代码
webClient.delete()
        .uri("/user/1")
        .retrieve()
        .bodyToMono(String.class)
        .block();

十四、下载文件

这是企业里非常常见的场景。


下载文件为 byte[]

java 复制代码
byte[] data = webClient.get()
        .uri("https://example.com/test.pdf")
        //如果需要携带token
    	.header(HttpHeaders.AUTHORIZATION,"Bearer xxxxxx")
        .retrieve()
        .bodyToMono(byte[].class)
        .block();

保存本地文件

java 复制代码
byte[] data = webClient.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(内存溢出)

流式下载

java 复制代码
Flux<DataBuffer> flux = webClient.get()
        .uri("/download/file")
        .retrieve()
        .bodyToFlux(DataBuffer.class);

DataBufferUtils.write(
        flux,
        Paths.get("D:/big.zip"),
        StandardOpenOption.CREATE
).block();

或者:

java 复制代码
webClient.get()
        .uri("https://example.com/big.zip")
        .retrieve()
        .bodyToFlux(DataBuffer.class)
        .map(DataBuffer::asByteBuffer)
        .doOnNext(buffer -> {
            // 写入文件
        })
        .blockLast();

十五、错误处理

普通写法

java 复制代码
webClient.get()
        .uri("/user")
        .retrieve()
        .bodyToMono(String.class)

如果:

text 复制代码
404
500

会抛异常。


onStatus 状态码错误处理

java 复制代码
String result = webClient.get()
        .uri("/user")
        .retrieve()
        .onStatus(
                HttpStatusCode::is4xxClientError,
                response -> Mono.error(new RuntimeException("4xx异常"))
        )
        .onStatus(
                HttpStatusCode::is5xxServerError,
                response -> Mono.error(new RuntimeException("5xx异常"))
        )
        .bodyToMono(String.class)
        .block();

try-catch

java 复制代码
try {
    String result = webClient.get()
            .uri("/test")
            .retrieve()
            .bodyToMono(String.class)
            .block();
} catch (Exception e) {
    e.printStackTrace();
}

十六、retrieve() 和 exchangeToMono() 区别

retrieve()(最常用)

适合:

  • 普通接口调用
  • 简洁开发

简单场景:

java 复制代码
.retrieve()
.bodyToMono(...)

exchangeToMono()

适合高级场景:

  • 获取状态码
  • 获取响应头、cookie
  • 自定义响应处理
java 复制代码
String result = webClient.get()
        .uri("/user")
        .exchangeToMono(response -> {
            if (response.statusCode().is2xxSuccessful()) {
                return response.bodyToMono(String.class);
            }

            return Mono.error(
                    new RuntimeException("请求失败")
            );
        })
        .block();

十七、 WebClient适合场景与不适合场景?

非常适合:

  • 微服务
  • 高并发
  • API 网关
  • 聚合接口
  • AI 调用
  • 并发请求多个服务
  • SSE/流式响应

不适合的场景:

如果你的项目:

text 复制代码
完全同步
低并发
传统 MVC

那么:

text 复制代码
RestTemplate / RestClient

可能更简单。

社区里也有很多开发者提到:

  • WebFlux 会增加复杂度
  • Mono / Flux 学习成本较高 (Reddit)

十八、WebClient 学习路线

建议按这个顺序学习:

  1. WebClient 基础 API
  2. Mono / Flux
  3. Reactor
  4. 异步编程
  5. 响应式编程
  6. Netty
  7. 背压(BackPressure)

十九、最常用写法总结

1.GET

java 复制代码
webClient.get()

2.POST

java 复制代码
webClient.post()

3.设置 header

java 复制代码
.header()

4.设置 body

java 复制代码
.bodyValue()

5.获取响应

java 复制代码
.retrieve()
.bodyToMono()

6.阻塞等待

java 复制代码
.block()

二十、完整实战示例

封装 HttpClientService

java 复制代码
@Service
public class HttpClientService {

    private final WebClient webClient;

    public HttpClientService(WebClient.Builder builder) {

        this.webClient = builder
                .baseUrl("https://api.example.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE,
                        MediaType.APPLICATION_JSON_VALUE)
                .build();
    }

    // GET
    public String getUser() {

        return webClient.get()
                .uri("/user/1")
                .retrieve()
                .bodyToMono(String.class)
                .block();
    }

    // POST
    public String login(LoginRequest request) {

        return webClient.post()
                .uri("/login")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(String.class)
                .block();
    }

    // token请求
    public String getWithToken(String token) {

        return webClient.get()
                .uri("/user/info")
                .header(HttpHeaders.AUTHORIZATION,
                        "Bearer " + token)
                .retrieve()
                .bodyToMono(String.class)
                .block();
    }
}

二十一、最后总结

WebClient 本质上:

text 复制代码
Spring 官方现代 HTTP 客户端

它最大的特点:

  • 非阻塞
  • 响应式
  • 高并发
  • 异步
  • 链式 API

企业中现在越来越多:

text 复制代码
微服务 + WebClient

的组合。

但它的核心难点其实不是 WebClient 本身,而是:

text 复制代码
Mono / Flux / Reactor
相关推荐
未若君雅裁10 小时前
Spring Cloud 组件全景与注册中心原理
后端·spring·spring cloud
Simon5231410 小时前
mybatis执行流程、关联映射、注解开发
java·开发语言·mybatis
神奇小汤圆10 小时前
从零搭建量化投资系统:用 Qlib 一行代码搞定均线分析
后端
冷雨夜中漫步10 小时前
SQLite 深度解析:在 Java/Spring 中的使用与H2、Derby对比
java·spring·sqlite
laufing10 小时前
Java 模板引擎 FreeMarker 入门教程:语法、内建函数与常用案例
java·freemarker
Moment10 小时前
开发Agent为什么必须先做意图识别?
前端·后端·面试
wengqidaifeng10 小时前
C++从菜鸟到强手:2.类和对象(上)—— 从结构体到类的跨越
java·开发语言·c++
自律懒人10 小时前
2026年AI编程工具横评:Trae、Cursor、Claude Code、Copilot X,同一需求谁更强?
java·copilot·ai编程
夕除11 小时前
spring boot 13
java·mysql·spring