一、什么是 WebClient?

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

WebClient 它底层基于:

  • Reactor
  • Netty
  • NIO

因此:

少量线程
处理大量请求

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

官方文档:


二、 WebClient 能解决什么问题?

它主要用于:

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

例如:

你的系统
   ↓
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:

线程发请求
↓
一直等待响应
↓
线程被占用

WebClient:

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

因此:

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

支持同步调用

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

.block()

变成同步调用。

因此:

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


2. 支持异步

可以同时请求多个接口:

Mono<User> userMono = webClient.get()...
Mono<Order> orderMono = webClient.get()...

最后组合:

Mono.zip(userMono, orderMono)

3. 链式 API 更现代

传统的 RestTemplate:

restTemplate.exchange(...)

而 WebClient:

webClient.get()
         .uri("/user")
         .retrieve()
         .bodyToMono(User.class);

更像:

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

五、WebClient 的核心对象

最重要的:

WebClient

它类似:

浏览器客户端

负责:

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

六、Mono 和 Flux 是什么?

WebClient 基于 Reactor。

两个核心类:

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

例如:

Mono<User>
Flux<User>

表示:

Mono<User>表示未来会返回一个 User
Flux<User>表示未来会返回多个 User

七、如何引入 WebClient?

Maven

Spring Boot 项目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

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


八、WebClient 的基本创建方式

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

WebClient webClient = WebClient.create();

或者:

WebClient webClient = WebClient.create("https://api.example.com");

或者:


WebClient webClient = WebClient.builder()
        .baseUrl("https://api.example.com")
        .build();

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

配置类

@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 示例

@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 框架)

注入使用

@Autowired
private WebClient webClient;

九、GET 带参数

请求

GET /user?page=1&size=10

代码

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();

最终请求:

https://api.example.com/user?page=1&size=10

代码解析

get()

表示 GET 请求:

webClient.get()

uri()

请求地址。

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

.uri("/user")

headers()

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


retrieve()

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

.retrieve()

bodyToMono()

响应转对象:

.bodyToMono(String.class)

block()

阻塞等待结果:

.block();

注意:

WebClient 本身是异步的。

调用:

.block()

才会变成同步等待。


十、GET 返回对象

User 类

@Data
public class User {

    private Long id;

    private String name;
}

调用

User user = webClient.get()
        .uri("/user")
        .retrieve()
        .bodyToMono(User.class)
        .block();

如果带参数和请求头:

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 请求示例

请求

POST /user
Content-Type: application/json

请求体:

{
  "name":"张三",
  "password":"123",
}

DTO

@Data
public class UserReq {
    private String name;
    private String password;
}

POST 代码

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();

如果是表单请求:

.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[] data = webClient.get()
        .uri("https://example.com/test.pdf")
        //如果需要携带token
    	.header(HttpHeaders.AUTHORIZATION,"Bearer xxxxxx")
        .retrieve()
        .bodyToMono(byte[].class)
        .block();

保存本地文件

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(内存溢出)

流式下载

Flux<DataBuffer> flux = webClient.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 状态码错误处理

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

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

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

retrieve()(最常用)

适合:

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

简单场景:

.retrieve()
.bodyToMono(...)

exchangeToMono()

适合高级场景:

  • 获取状态码
  • 获取响应头、cookie
  • 自定义响应处理
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/流式响应

不适合的场景:

如果你的项目:

完全同步
低并发
传统 MVC

那么:

RestTemplate / RestClient

可能更简单。

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

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

十八、WebClient 学习路线

建议按这个顺序学习:

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

十九、最常用写法总结

1.GET

webClient.get()

2.POST

webClient.post()

3.设置 header

.header()

4.设置 body

.bodyValue()

5.获取响应

.retrieve()
.bodyToMono()

6.阻塞等待

.block()

二十、完整实战示例

封装 HttpClientService

@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 本质上:

Spring 官方现代 HTTP 客户端

它最大的特点:

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

企业中现在越来越多:

微服务 + WebClient

的组合。

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

Mono / Flux / Reactor

更多推荐