Spring Boot中CompletableFuture抛自定义异常始终返回500如何解决
问题根因
CompletableFuture异步线程中抛出的异常会被自动封装为java.util.concurrent.CompletionException,你自定义的CustomException会作为该包装异常的根因(cause)存在- 全局异常处理器的
@ExceptionHandler(CustomException.class)仅能匹配直接抛出的CustomException实例,无法匹配外层包裹的CompletionException,因此异常会命中通用Exception处理逻辑,返回500状态码 - 额外注意:你的
CustomExceptionHandler类不需要继承Throwable,@ControllerAdvice注解的类无继承要求,该继承逻辑可直接删除。
解决方案
方案1:新增CompletionException专门处理逻辑(推荐)
在全局异常处理器中添加优先级最高的CompletionException处理方法,主动解包判断根因类型:
@ControllerAdvice public class CustomExceptionHandler { @Order(Ordered.HIGHEST_PRECEDENCE) @ExceptionHandler(value = CompletionException.class) public ResponseEntity<ErrorResponse> handleCompletionException(CompletionException ex) { Throwable rootCause = ex.getCause(); // 根因为自定义异常时走400逻辑 if (rootCause instanceof CustomException) { CustomException customEx = (CustomException) rootCause; ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("400"); errorResponse.setDescription(customEx.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } // 其他异常走通用500逻辑 ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("500"); errorResponse.setDescription(rootCause != null ? rootCause.getMessage() : ex.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } // 保留原有两个异常处理方法即可 @Order(Ordered.HIGHEST_PRECEDENCE) @ExceptionHandler(value = CustomException.class) public ResponseEntity<ErrorResponse> handleCustomException(CustomException ex) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("400"); errorResponse.setDescription(ex.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = Exception.class) public ResponseEntity<ErrorResponse> handleGenericExceptions(Exception exception) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setCode("500"); errorResponse.setDescription(exception.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } }
方案2:异步回调中主动解包抛出原始异常
在CompletableFuture的回调逻辑中对异常做解包处理,直接抛出原始自定义异常,让全局异常处理器可以直接匹配:
CompletableFuture.supplyAsync(() -> { return someMethod(); }).whenCompleteAsync((response, throwable) -> { if (throwable != null) { // 解包CompletionException if (throwable instanceof CompletionException && throwable.getCause() != null) { throw (RuntimeException) throwable.getCause(); } throw (RuntimeException) throwable; } });
内容的提问来源于stack exchange,提问作者Prabhakaran
相关产品推荐
相关产品推荐

