Spring Boot中DeferredResult的onError回调如何正确触发?
为什么DeferredResult的onError回调没触发?怎么解决?
首先得明确一个核心点:DeferredResult的onError回调并不是用来捕获你自己异步线程里的业务异常的。它主要处理的是DeferredResult自身的框架级错误,比如请求超时、或者Spring在处理DeferredResult生命周期时抛出的异常。
你当前的问题根源在于:异步线程(ForkJoinPool里的线程)抛出的异常,和Spring管理的请求线程是完全隔离的,Spring无法自动捕获这个线程的异常并触发onError。而且你在catch块里又把异常重新抛出,这个异常只会被ForkJoinPool的线程池处理,不会关联到DeferredResult上,导致DeferredResult一直处于未完成状态,超时后就返回了默认的503错误。
解决方案:手动处理异步线程的异常,主动通知DeferredResult
你需要在异步任务的catch块里,手动调用DeferredResult的setErrorResult()方法,直接把错误响应设置进去,这样DeferredResult会立即完成并返回错误给客户端。如果需要复用onError里的逻辑,也可以在这个过程中调用相关处理逻辑,但核心是要主动触发DeferredResult的错误状态。
修改后的代码示例:
@GetMapping("/getErrorResults") public DeferredResult<ResponseEntity<?>> getDeferredResultsError(){ final String METHOD_NAME = "getDeferredResultsError"; logger.info("START : {}",METHOD_NAME); // 主动设置超时时间,避免长时间挂起 DeferredResult<ResponseEntity<?>> deferredOutput = new DeferredResult<>(5000L); ForkJoinPool.commonPool().submit(() -> { logger.info("processing in separate thread"); int age = 0; try { age = age / 0; // 业务逻辑正常执行时,设置正常返回结果 deferredOutput.setResult(ResponseEntity.ok(age)); }catch(Exception e) { logger.error("we got some error", e); // 手动设置错误结果,触发DeferredResult完成 deferredOutput.setErrorResult( ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("业务处理异常: " + e.getMessage()) ); } logger.info("after try catch block"); }); // onError这里用来处理超时或者框架层面的错误 deferredOutput.onError((Throwable t) -> { logger.error("<<< HERE !!! >>> 框架级错误触发", t); deferredOutput.setErrorResult( ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("请求处理异常: " + t.getMessage()) ); }); logger.info("done"); return deferredOutput; }
关键说明:
setErrorResult()的作用:调用这个方法会立即标记DeferredResult为完成状态,Spring会把你传入的错误结果作为HTTP响应返回给客户端,不会再等待超时。onError的适用场景:当DeferredResult超时(比如你设置了超时时间但没及时设置结果)、或者Spring在处理DeferredResult时发生框架内部错误时,这个回调才会被触发。- 避免未完成的DeferredResult:如果不手动设置结果(正常或错误),DeferredResult会一直处于挂起状态,直到超时,此时Spring会返回默认的503错误,这就是你之前遇到的情况。
内容的提问来源于stack exchange,提问作者satish marathe
相关产品推荐
相关产品推荐

