Spring WebFlux 与 WebClient 使用指南及最佳实践

2025-07-03 17:50

本文主要是介绍Spring WebFlux 与 WebClient 使用指南及最佳实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,...

Spring WebFlux 与 WebClient 使用指南

1. WebClient 概述

WebClient 是 Spring WebFlux 模块提供的非阻塞、响应式 HTTP 客户端,基于 Project Reactor 实现,适用于高并发场景。
核心优势

  • 支持异步非阻塞 I/O,提升吞吐量。
  • 链式 API 设计,便于组合操作。
  • 集成 Spring 生态,支持自动编解码(jsON、XML)。

2. 核心依赖配置

pom.xml 中添加依赖:

<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://api.example.com") // 基础 URL
            .defaultHeader("Accept", "application/json")
            .clientConnector(new ReactorClientHttpConnector(
                HttpClient.create()
                    .responseTimeout(Duration.ofSeconds(30)) // 响应超时
            )
            .build();
    }
}

3.2 临时创建(按需使用)

WebClient client = WebClient.create(www.chinasem.cn"https://api.example.com");

4. 发送 HTTP 请求

4.1 GET 请求(携带 Token)

public Mono<User> getUser(String id, String token) {
    return webClient.get()
        .uri("/users/{id}", id) // 路径参数
        .header("Token-Test", token) // 自定义 Token
        .retrieve()
        .bodyToMono(User.class); // 解析为对象
}

4.2 POST 请求(发送数组 Body)

public Mono<String> postUsers(List<User> users, String token) {
    return webClient.post()
        .uri("/users/BATch")
        .header("Token-Test", token)
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(users) // 发送 List 集合
        .retrieve()
        .bodyToMono(String.class);
}

5. 错误处理

5.1 HTTP 错误处理(onStatus)

.onStatus(HttpStatus::isError, response ->
    response.bodyToMono(String.class)
        .flatMap(errorBody -> {
            String msg = String.format("状态码: %d, 错误信息: %s", 
                response.rawStatusCode(), errorBody);
            log.error(msg);
            return Mono.error(new ServiceException(msg));
        })
)

5.2 非 HTTP 错误处理(doOnError)

.doOnError(error -> {
    if (!(error instanceof ServiceException)) {
        log.error("非 HTTP 错误: {}", error.getMessage());
    }
})

6. 同步与异步调用

6.1 异步调用(subscribe)

webClient.get()
    .uri("/data")
    .retrieve()
    .bodyToMono(String.class)
    .subscribe(
        data -> log.info("成功: {}", data),
        error -> log.error("失败: {}", error)
    );

6.2 同步调用(block,仅用于测试或特殊场景)

try {
    String result = webClient.get()
        .uri("/data")
        .retrieve()
        .bodyToMono(String.class)
        .block(Duration.ofSeconds(10)); // 阻塞等待
} catch (Exception e) {
    log.error("请求失败", e);
}

7. 统一响应结构

7.1 定义统一响应类

public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "Success", data);
    }
    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }
}

7.2 转换响应

public Mono<ApiResponse<User>> getUser(String id) {
    return webClient.get()
        .uri("/users/{id}", id)
        .retrieve()
        .bodyToMono(User.class)
        .map(ApiResponse::success) //China编程 包装为成功响应
        .onErrorResume(e -> 
            Mono.just(ApiResponse.error(500, e.getMessage()))
        );
}

8. 日志与监控

8.1 成功日志

.doOnSuccess(response -> 
    log.info("请求成功: {}", response)
)

8.2 错误日志

.doOnError(error -> 
    log.error("请求失败: {}", error.getMessage())
)

9. 高级配置

9.1 超时与重试

.clientConnector(new ReactorClientHttpConnector(
    HttpClient.create()
        .responseTimeout(Duration.ofSeconds(30)) // 响应超时
)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))) // 指数退避重试

9.2 连接池配置

HttpClient.create()
    .baseUrl("https://api.example.com")
    .tcpConfiguration(tcpClient -> 
        tcpClient.option(Channhttp://www.chinasem.cnelOption.CONNECT_TIMEOUT_MILLIS, 5000)
    )

10. 常见问题与最佳实践

10.1 避免手动调用subscribe

  • 错误示例
    // Service 层中手动调用 subscribe(不推荐)
    public void sendData() {
        webClient.post().subscribe(); // 可能导China编程致资源泄漏
    }
  • 正确做法
    在 Controller 或调用方返回 Mono/Flux,由框架处理订阅。

10.2 统一异常处理

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Mono<ApiResponse<?>> handleServiceException(ServiceException e) {
        return Mono.just(ApiResponse.error(500, e.getMessage()));
    }
}

10.3 性能优化

  • 复用 WebClient 实例:避免频繁创建新实例。
  • 合理设置超时:根据接口 SLA 调整响应和连接超时。

附录:完整代码示例

发送 POST 请求并处理错误

public Mono<ApiResponse<String>> syncData(List<User> users, String token) {
    String uri = UriComponentsBuilder.fromUriString("https://api.example.com")
        .path("/batch")
        .queryParam("source", "web")
        .queryParamIfPresent("type", Optional.ofNullable("test".equals(activeProfile) ? "test" : null))
        .build()
        .toUriString();
    return webClient.post()
        .uri(uri)
        .header("Token-Test", token)
        .headers(headers -> {
             if ("test".equals(activeProfile)) {
                 headers.add("type", "test"); // 仅测试环境添加
             }
         })
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(users)
        .retrieve()
        .onStatus(HttpStatus::isError, response ->
                response.bodyToMono(String.class)
                        .flatMap(error -> {
                            String msg = String.format("状态码:%d, 错误信息: %s", response.rawStatusCode(), error);
                            log.error(msg);
                            return Mono.error(new RuntimeException(msg));
                        })
        )
        .bodyToMono(ApiResponse.class)  // 解析为自定义响应对象
         // 检查业务状态码
        .flatMap(result -> {
            if (result.getStatusCode() != 200) {
                String msg = String.format("业务错误: code=%d, message=%s",
                        result.getStatusCode(), result.getMessage());
                log.error(msg);
                return Mono.error(new ServiceException(msg));
            }
            return Mono.just(result);
        })
         // 成功记录日志
        .doOnSuccess(success -> log.info("请求成功"))
         // 失败记录日志
        .doOnError(error -> log.error("失败", error.getMessage()编程))
        .onErrorResume(e -> 
           Mono.just(ApiResponse.error(500, e.getMessage()))
        );
}

通过本文档,您可全面掌握 WebClient 的核心用法、错误处理策略及性能优化技巧。建议结合项目需求灵活调整配置,遵循响应式编程最佳实践。

到此这篇关于Spring WebFlux 与 WebClient 使用指南及最佳实践的文章就介绍到这了,更多相关Spring WebFlux WebClient 使用内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于Spring WebFlux 与 WebClient 使用指南及最佳实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155289

相关文章

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空