告别 try-catch:响应式异常处理机制
在传统的同步编程中,我们习惯用 try-catch 来捕获异常。但在响应式编程中,由于代码是声明式的、异步执行的,当异常在 map 或 flatMap 内部抛出时,它不会中断当前线程,而是作为一个"错误信号(onError)"沿着数据流向下游传播。如果这个信号没有被处理,最终会导致整个订阅被终止。
Reactor 提供了一套强大的操作符来优雅地处理这些错误:
-
onErrorReturn(优雅降级):当发生异常时,直接返回一个默认的兜底值,保证流不会中断。
javaFlux.just(1, 2, 0, 4) .map(i -> 10 / i) // 遇到 0 时会抛出 ArithmeticException .onErrorReturn(-1) // 捕获异常,返回默认值 -1 .subscribe(System.out::println); // 输出: 10, 5, -1 -
onErrorResume(切换备用流):当发生异常时,根据异常类型切换到另一个备用的 Publisher(数据流)。这在处理外部服务调用失败时非常有用。
javafetchFromPrimaryService() .onErrorResume(TimeoutException.class, e -> fetchFromBackupService()) // 主服务超时,自动切换备用服务 .subscribe(System.out::println); -
onErrorMap(异常转换):将底层的异常(如数据库报错)转换为业务层更容易理解的自定义异常。
javauserRepository.findById(id) .onErrorMap(DataAccessException.class, e -> new BusinessException("用户数据获取失败")) .subscribe();
全局异常拦截:@ControllerAdvice 的响应式改造
除了流级别的处理,Spring WebFlux 依然支持我们熟悉的 @ControllerAdvice 进行全局异常拦截。需要注意的是,在 WebFlux 中,处理方法的返回值应该是响应式的 Mono。
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Mono<ResponseEntity<ErrorResponse>> handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
return Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error));
}
}
响应式自动化测试利器:WebTestClient
在 Spring WebFlux 中,传统的 MockMvc 无法处理异步流,因此官方提供了 WebTestClient。它基于 WebClient 构建,专门用于对 WebFlux 端点进行非阻塞的断言测试。
实战示例:使用 @WebFluxTest 进行控制器测试
java
@WebFluxTest(UserController.class) // 仅加载 WebFlux 相关的上下文,启动极快
class UserControllerTest {
@Autowired
private WebTestClient webTestClient;
@MockBean
private UserService userService; // 模拟 Service 层依赖
@Test
void shouldReturnUserWhenExists() {
// 1. 定义 Mock 行为
Mockito.when(userService.findById("1"))
.thenReturn(Mono.just(new User("1", "Alice")));
// 2. 发起请求并断言
webTestClient.get().uri("/api/users/1")
.exchange() // 执行请求
.expectStatus().isOk() // 断言 HTTP 状态码为 200
.expectBody() // 断言响应体内容
.jsonPath("$.name").isEqualTo("Alice");
}
}
响应式流的单元测试:StepVerifier
如果要测试 Service 层返回的 Flux 或 Mono,WebTestClient 就不适用了。此时我们需要引入 reactor-test 依赖,并使用 StepVerifier。它是验证异步序列的绝对利器。
实战示例:验证 Flux 流的行为
java
@Test
void testUserStream() {
Flux<String> userStream = userService.getActiveUsers();
StepVerifier.create(userStream)
.expectNext("Alice") // 期望第一个元素是 Alice
.expectNextMatches(name -> name.startsWith("B")) // 期望第二个元素以 B 开头
.expectNextCount(3) // 期望接下来还有 3 个元素
.expectComplete() // 期望流正常结束
.verify(Duration.ofSeconds(5)); // 设置超时时间,防止流卡死
}
本篇小结:响应式编程中的异常处理分为"流级别的操作符降级"和"全局级别的统一拦截"。而在测试方面,WebTestClient 和 StepVerifier 是验证响应式逻辑正确性的左膀右臂。
下一步预告:至此,我们已经掌握了 Reactor 的核心语法。下一篇笔记,我们将正式回到 Spring WebFlux 框架,对比"基于注解的 Controller"与"函数式端点(Router Functions)"两种编程模型,并学习如何使用 WebClient 发起非阻塞的外部 HTTP 调用。准备好进入真正的 Web 开发了吗?