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或像onErrorResume
、onErrorReturn
或onErrorMap
这样的反应式操作符,当由Mono.error()
发出信号时。
4.2. 惰性初始化
Mono.error()
现在支持对异常的惰性实例化,在构建异常涉及资源密集型操作的情况下特别有利。
4.3. 反应式错误处理
Mono.error()
与反应式编程范式很好地结合,便于在反应式流中实现反应式错误处理。
5. 总结
在这篇文章中,我们讨论了在Spring WebFlux的反应式应用中抛出异常和使用Mono.error()
进行错误处理的基本差异;尽管两者都有信号错误的目的,但在控制流程和与反应式管道的集成上存在显著差异。
抛出异常中断执行流程,将控制权转移到最近的异常处理器,适用于命令式代码路径。相反,Mono.error()
与反应式流无缝集成,允许异步错误信号而不停止执行流程。
在使用Spring WebFlux开发反应式应用时,根据上下文和需求选择合适的错误处理机制至关重要。我们在反应式管道中使用Mono.error()
以保持其反应式特性,在命令式代码路径中使用异常。如往常一样,本教程的源代码可在GitHub上找到。