【WebFlux】第五篇 —— 两种编程模型与 WebClient

踏入 WebFlux 的世界:两种编程模型

掌握了 Project Reactor 的核心语法后,我们终于迎来了 Spring WebFlux 框架层面的开发。与 Spring MVC 类似,WebFlux 同样提供了两种截然不同的编程模型,开发者可以根据团队习惯和业务场景自由选择。

  • 基于注解的控制器(Annotated Controllers) :这是传统 Spring MVC 开发者最熟悉的模式。你依然可以使用 @RestController、@GetMapping 等注解,最大的区别在于方法的返回值需要替换为 Mono 或 Flux。这种模式学习成本极低,适合快速迁移现有的业务代码。

    java 复制代码
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @GetMapping("/{id}")
        public Mono<User> getUser(@PathVariable String id) {
            // 返回一个异步的 Mono 数据流
            return userService.findById(id);
        }
    }
  • 函数式端点(Functional Endpoints) :这是 WebFlux 引入的轻量级、纯函数式的编程模型。它将"路由(Router)"与"处理逻辑(Handler)"彻底解耦。路由负责匹配请求,Handler 负责处理请求并返回 Mono。这种模式代码更加纯粹,非常适合构建高度模块化的微服务或网关。

    java 复制代码
    // 1. 定义 Handler
    public Mono<ServerResponse> getUser(ServerRequest request) {
        String id = request.pathVariable("id");
        return userService.findById(id)
            .flatMap(user -> ServerResponse.ok().bodyValue(user));
    }
    
    // 2. 定义 Router
    @Bean
    public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
        return RouterFunctions.route(GET("/api/users/{id}"), handler::getUser);
    }

响应式 HTTP 客户端:WebClient 登场

在传统的 Spring MVC 中,我们使用 RestTemplate 进行外部 HTTP 调用,但它是一个阻塞式的客户端。在 WebFlux 的响应式世界中,官方推荐使用 WebClient 作为替代方案。WebClient 是完全非阻塞的,支持背压和流式传输。

WebClient 的创建与基础配置 :

通常我们会通过 WebClient.Builder 注入并配置全局参数(如 BaseUrl、默认请求头、超时时间等),将其注册为 Spring Bean。

java 复制代码
@Bean
public WebClient webClient(WebClient.Builder builder) {
    return builder
        .baseUrl("https://api.example.com")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();
}

发起非阻塞请求(GET / POST):

java 复制代码
// GET 请求:获取单个对象
public Mono<User> fetchUser(String id) {
    return webClient.get()
        .uri("/users/{id}", id)
        .retrieve() // 提取响应
        .bodyToMono(User.class); // 将响应体转换为 Mono<User>
}

// POST 请求:提交数据
public Mono<User> createUser(User user) {
    return webClient.post()
        .uri("/users")
        .bodyValue(user) // 传入请求体
        .retrieve()
        .bodyToMono(User.class);
}

避坑警告:严禁在响应式管道中调用 block()

这是初学者从传统开发转向 WebFlux 时最容易犯,也是最致命的错误!

WebClient 返回的是 Mono 或 Flux,如果你为了获取里面的数据而调用了 .block() 方法,当前线程就会被强制阻塞,直到响应返回 。在 WebFlux 默认使用的 Netty 服务器中,I/O 线程的数量是非常有限的(通常等于 CPU 核心数)。如果你在 Controller 或 Service 中使用了 block(),就会直接阻塞这些宝贵的 I/O 线程,导致整个服务器瞬间失去响应能力,并发能力甚至不如传统的 Tomcat。

最佳实践:保持全链路的响应式。永远使用 flatMap、map 等操作符来传递和处理数据,将 Mono 和 Flux 一路透传到 Controller 层,交由框架去异步订阅。

本篇小结:WebFlux 提供了注解式和函数式两种灵活的 Web 开发模式。在对外发起 HTTP 请求时,必须使用非阻塞的 WebClient,并时刻警惕 block() 方法对 I/O 线程的致命污染。

下一步预告:外部调用解决了,那数据库层呢?传统的 JDBC 和 JPA 都是阻塞的,如何在数据层也实现全链路非阻塞?下一篇笔记,我们将深入探讨 Spring Data Reactive 与 R2DBC,打通响应式数据访问的最后一公里。

相关推荐
Sarvartha6 小时前
final 关键字
java·开发语言
2601_952047796 小时前
单一策略深度教程:用轻易云把集成任务的报错实时推送到钉钉机器人
java·机器人·钉钉
数据狐(Datafox)7 小时前
淘宝图片搜索 API 落地实战:基于以图搜货搭建跨境电商选品系统
java·大数据·微服务
Escalating_xu9 小时前
【C 语言】数据在内存中的存储:补码、大小端、整型陷阱与 IEEE 754 全解析
java·c语言·网络
灯澜忆梦9 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
一个风轻云淡9 小时前
GCC 和 GDB命令简单解读
java·linux·前端
省钱兄--zs9 小时前
西安24小时自助健身房解决方案实战:系统开发与部署全流程指南
java·spring boot·系统架构·intellij-idea·需求分析
资料库0110 小时前
Linux 8个常见运维故障排查
java·linux·运维