1. 概述

在Spring WebFlux的反应式编程中,错误处理是一个关键方面。开发者通常依赖两种主要方法来处理错误:抛出异常或使用Project Reactor提供的Mono.error()方法。这两种方法都用于表示错误,但它们有独特的特性和用例。

在这篇教程中,我们将解释在Spring WebFlux中抛出异常与使用Mono.error()的区别。我们会提供示例Java代码,以便更好地理解。

2. 传统方法:抛出异常

多年来,抛出异常一直是Java应用中管理错误的可靠方式。这是一种简单的方法,可以中断程序的正常流程,并将错误传达给应用程序的更高层。Spring WebFlux与这种传统的错误处理方法无缝集成,允许开发者在其反应式端点中抛出异常。下面的代码展示了传统的处理方式:

public Mono<User> getUserByIdThrowingException(String id) {
    User user = userRepository.findById(id);
    if (user == null) {
       throw new NotFoundException("User Not Found");
    }
    return Mono.justOrEmpty(user);
}

在这个特定场景中,getUserByIdThrowingException()方法尝试根据UserRepository提供的ID检索用户数据。如果找不到用户,该方法会抛出一个NotFoundException,在反应式管道中表示错误。

为了进行单元测试,我们导入了org.junit.jupiter.api.Assertions中的assertThrows方法。这个测试会检查getUserByIdThrowingException()是否在数据库中找不到用户时抛出NotFoundException。我们使用assertThrows配合lambda执行应抛出异常的方法调用。

如果抛出了异常,代码会验证抛出的异常类型符合预期。但如果方法没有抛出异常,测试就会失败:

@Test
public void givenNonExistUser_whenFailureCall_then_Throws_exception() {
    assertThrows(
        NotFoundException.class,
        () -> userService.getUserByIdThrowingException("3")
    );
}

3. 接受反应性:Mono.error()

与传统的抛出异常方式相反,Project Reactor通过Mono.error()方法引入了反应式替代方案。此方法生成一个立即以错误信号终止的Mono,完美契合了反应式编程范式。

让我们看看使用Mono.error()修改后的例子:

public Mono<User> getUserByIdUsingMonoError(String id) {
    User user = userRepository.findById(id);
    return (user != null)
      ? Mono.justOrEmpty(user)
      : Mono.error(new NotFoundException("User Not Found"));
}

为了维护良好的用户体验和一致的反应式流,我们在数据库中找不到用户时,使用Mono.error()而不是直接抛出异常。

这是该方法的单元测试:

@Test
 public void givenNonExistUser_whenFailureCall_then_returnMonoError() {
    Mono result = userService.getUserByIdUsingMonoError("3");
    StepVerifier.create(result)
      .expectError(NotFoundException.class)
      .verify();
}

4. 理解关键差异和用例

4.1. 控制流程中断

我们使用try-catch或像onErrorResumeonErrorReturnonErrorMap这样的反应式操作符,当由Mono.error()发出信号时。

4.2. 惰性初始化

Mono.error()现在支持对异常的惰性实例化,在构建异常涉及资源密集型操作的情况下特别有利。

4.3. 反应式错误处理

Mono.error()与反应式编程范式很好地结合,便于在反应式流中实现反应式错误处理。

5. 总结

在这篇文章中,我们讨论了在Spring WebFlux的反应式应用中抛出异常和使用Mono.error()进行错误处理的基本差异;尽管两者都有信号错误的目的,但在控制流程和与反应式管道的集成上存在显著差异。

抛出异常中断执行流程,将控制权转移到最近的异常处理器,适用于命令式代码路径。相反,Mono.error()与反应式流无缝集成,允许异步错误信号而不停止执行流程。

在使用Spring WebFlux开发反应式应用时,根据上下文和需求选择合适的错误处理机制至关重要。我们在反应式管道中使用Mono.error()以保持其反应式特性,在命令式代码路径中使用异常。如往常一样,本教程的源代码可在GitHub上找到。