WebFlux 响应式入门:WebClient 与注解式控制器差异
传统 MVC 是"一个请求占一个线程"阻塞模型,线程常在等待 IO 时闲置。WebFlux 是 Spring 5+ 的响应式 Web 栈:少量事件循环线程支撑海量并发请求,IO 等待时不占线程。Boot 3 中它默认跑在 Netty 上。本章对比它与 MVC 注解式控制器的差异,并给出 WebClient 用法。
引入与取舍
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
不要与 spring-boot-starter-web 同时引入,二者冲突会让自动配置无所适从。什么时候值得用:网关、高并发 IO 密集(大量调用外部 HTTP/缓存);什么时候别硬上:链路里满是阻塞 JDBC、同步第三方 SDK 时,收益有限复杂度翻倍——已有 MVC 服务不必为赶时髦迁移。
注解式控制器:注解一样,返回类型不同
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService; // 返回响应式类型
@GetMapping("/{id}")
public Mono<OrderDto> get(@PathVariable String id) {
return orderService.findById(id); // Mono:0 或 1 个结果
}
@GetMapping
public Flux<OrderDto> list() {
return orderService.findAll(); // Flux:0..N 个结果(流式)
}
}
与 MVC 的关键差异:
- 线程模型:MVC 每请求占一条 servlet 线程直到响应结束;WebFlux 请求由 Netty 事件循环驱动,无结果时不占线程;
- 响应式类型:方法返回 Mono/Flux 而非直接对象,框架负责订阅;操作符 map/flatMap/filter 与 Java Stream 形似,但懒执行 + 背压(消费者慢则上游放慢),"nothing happens until you subscribe";
- 阻塞即原罪:在响应式管道里调
Thread.sleep、JDBC、阻塞 RestTemplate 会卡死事件循环线程,是性能反模式; - 事务/安全上下文:@Transactional 事务绑定线程,@AuthenticationPrincipal 之类依赖 ThreadLocal 的组件在响应式下要改用 Reactive 变体(R2DBC、ReactiveSecurityContext),细节以官方文档为准。
WebClient:非阻塞 HTTP 客户端
WebFlux 项目里替代 RestTemplate 的标配:
WebClient client = WebClient.builder()
.baseUrl("https://example.com")
.build();
Mono<UserDto> mono = client.get()
.uri("/api/users/{id}", 1L)
.retrieve() // 取响应
.bodyToMono(UserDto.class); // 反序列化为 Mono
mono.subscribe(u -> System.out.println(u)); // 输出:UserDto(id=1, name=张三)
- 整条链非阻塞,多个外部调用可用
Mono.zip(...)/Flux.merge(...)并发聚合,比串行阻塞快得多; - 处理错误用
onErrorResume(...)回退或.retrieve().bodyToMono(...)配合自定义状态处理; - 在 MVC 工程里 WebClient 也能用(它是独立的响应式客户端),别把 WebClient 与 WebFlux 划等号;
- 测试用
WebTestClient发起请求断言,写法与 MockMvc 相近。
选择建议
网关、BFF、实时推送(SSE/WebSocket)、高并发 IO 服务 → WebFlux 收益明显;内部 CRUD、重 DB 逻辑 → MVC 更务实。响应式对团队心智要求高,先想清楚问题再选技术。
小结:WebFlux 以 Mono/Flux + 事件循环换高并发;注解式控制器写法与 MVC 几乎一致,但阻塞调用、ThreadLocal 依赖是红线;HTTP 调用统一用非阻塞的 WebClient。