【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 返回的是 MonoFlux,如果你为了获取里面的数据而调用了 .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,打通响应式数据访问的最后一公里。

相关推荐
增量星球18 小时前
《持续交付2.0系列六》业务需求协作管理
java·运维·自动化·devops·持续部署·持续集成
重生之我是Java开发战士18 小时前
【Java EE】Spring IoC 与 DI
java·spring·java-ee
SimonKing19 小时前
阿里要求全员卸载 Claude Code:事件始末与深层逻辑
java·后端·程序员
云烟成雨TD19 小时前
Agent Scope Java 2.x 系列【42】2.0 GA 正式发布:从透明开发迈向智能体系统工程
java·人工智能·agent
猫猫不是喵喵.19 小时前
SpringBoot自动装配原理
java·spring boot·后端
刘小八19 小时前
Redis 缓存一致性:一文讲透延时双删原理、并发时序
java·数据库·redis·缓存
吃饱了得干活19 小时前
从0到1实现消息已读未读:从基础设计到高并发架构
java·后端·架构
r_oo_ki_e_19 小时前
java微服务
java·微服务
weixin_BYSJ198719 小时前
django在线图书销售平台---附源码16192
java·javascript·spring boot·python·django·flask·php