Java响应式编程中抛出异常无法在控制器捕获的问题排查
问题分析与解决方案
你的代码存在几个核心问题,导致响应式流中的异常无法被捕获:
1. 同步try-catch无法捕获响应式流内的异常
响应式编程中,Mono/Flux操作符(比如map)内抛出的异常是作为流的错误信号存在的,并非同步抛出的异常。普通的try-catch块只能捕获当前线程同步执行时的异常,根本无法触及流内部异步执行时抛出的异常。
2. 响应式流未被触发执行
在MyClass.getData()方法中,你只是创建了Mono<String>对象,但没有触发流的执行:
- 如果是WebFlux控制器,需要直接返回
Mono对象,由框架自动处理订阅; - 如果是普通方法,需要调用
subscribe()(异步执行)或block()(同步阻塞,Web环境不推荐)来启动流。
流未执行的情况下,自然不会触发任何异常。
3. 服务层的try-catch完全无效
MyService.getResponseData()中的try-catch包裹的是Mono的创建代码,而map操作符内的异常是在流订阅执行阶段才会抛出的,这个try-catch根本无法捕获到这些异常。
修正后的代码示例
控制器层(正确处理响应式异常)
如果是WebFlux控制器,直接返回Mono并通过响应式错误操作符处理异常:
public class MyController { public Mono<String> getData() { return myService.getResponseData(1) // 处理CustomException类型的异常 .onErrorResume(CustomException.class, e -> { // 这里添加CustomException的专属处理逻辑 return Mono.just("Custom异常处理后的返回结果"); }) // 处理其他所有类型的异常 .onErrorResume(Exception.class, e -> { // 这里添加通用异常处理逻辑 return Mono.just("通用异常处理后的返回结果"); }); } }
服务层(移除无效try-catch,让异常流入错误信号)
public class MyService { public Mono<String> getResponseData(int number) { return myDbDTO1.table1(number) .map(result -> { if (validatingSomeCondition) { throw new CustomException("Invalid data!"); } return result; }) .zipWhen(result -> myDbDTO2.table2(number).collectList()) .map(m1 -> mapResults(m1.getT1(), m1.getT2())); } }
mapResults方法(保持原有逻辑即可)
该方法内同步抛出的异常会被map操作符捕获,自动转为流的错误信号向下传递:
public Result mapResults(Result1 res1, Result2 res2) { try { // 合并两个对象的逻辑 return new Result(res1, res2); } catch (Exception e) { throw new CustomException("Invalid data!"); } }
关键注意事项
- 响应式异常处理必须使用Reactor提供的错误操作符:
onErrorResume(替换错误信号为新的流)、onErrorReturn(返回默认值)、onErrorMap(转换异常类型)等。 - 永远不要用同步
try-catch包裹响应式流的操作符,这是响应式编程的常见误区。 - 确保流被正确订阅:WebFlux环境下返回
Mono/Flux即可,非Web环境需调用subscribe()或block()启动流。
内容的提问来源于stack exchange,提问作者Sweety
相关产品推荐
相关产品推荐

