Spring Boot2从入门到实战:统一异常处理

都说管理的精髓就是“制度管人,流程管事”。而所谓流程,就是对一些日常工作环节、方式方法、次序等进行标准化、规范化。且不论精不精髓,在技术团队中,对一些通用场景,统一规范是必要的,只有步调一致,才能高效向前。如前后端交互协议,如本文探讨的异常处理。

 

1. Spring Mvc中的异常处理

在spring mvc中,跟异常处理的相关类大致如下

 

上图中,spring mvc中处理异常的类(包括在请求映射时与请求处理过程中抛出的异常),都是 HandlerExceptionResolver 接口的实现,并且都实现了 Ordered 接口。与拦截器链类似,如果容器中存在多个实现了 HandlerExceptionResolver 接口的异常处理类,则它们的 resolveException 方法会被依次调用,顺序由order决定,值越小的先执行,只要其中一个调用返回不是null,则后续的异常处理将不再执行。

各实现类简单介绍如下:

DefaultHandlerExceptionResolver: 这个是默认实现,处理Spring定义的各种标准异常,将其转换为对应的Http Status Code,具体处理的异常参考 doResolveException 方法

ResponseStatusExceptionResolver:用来支持@ResponseStatus注解使用的实现,如果自定义的异常通过@ResponseStatus注解进行了修饰,并且容器中存在ResponseStatusExceptionResolver的bean,则自定义异常抛出时会被该bean进行处理,返回注解定义的Http Status Code及内容给客户端

ExceptionHandlerExceptionResolver:用来支持@ExceptionHandler注解使用的实现,使用该注解修饰的方法来处理对应的异常。不过该注解的作用范围只在controller类,如果需要全局处理,则需要配合@ControllerAdvice注解使用。

SimpleMappingExceptionResolver:将异常映射为视图

HandlerExceptionResolverComposite:就是各类实现的组合,依次执行,只要其中一个处理返回不为null,则不再处理。

因为本文主要是对spring boot如何对异常统一处理进行探讨,所以以上只对各实现做了基本介绍,更加详细的内容可查阅相关文档或后续再补上。

2. Spring Boot中如何统一异常处理

通过第一部分介绍,可以使用@ExceptionHandler + @ControllerAdvice 组合的方式来实现异常的全局统一处理。对于REST服务来说,spring mvc提供了一个抽象类 ResponseEntityExceptionHandler, 该类类似于上面介绍的 DefaultHandlerExceptionResolver,对一些标准的异常进行了处理,但不是返回 ModelAndView对象, 而是返回 ResponseEntity对象。故我们可以基于该类来实现REST服务异常的统一处理
定义异常处理类 BaseWebApplicationExceptionHandler 如下:

@RestControllerAdvice public class BaseWebApplicationExceptionHandler extends ResponseEntityExceptionHandler { private boolean includeStackTrace; public BaseWebApplicationExceptionHandler(boolean includeStackTrace){ super(); this.includeStackTrace = includeStackTrace; } private final Logger logger = LoggerFactory.getLogger(getClass()); @ExceptionHandler(BizException.class) public ResponseEntity<Object> handleBizException(BizException ex) { logger.warn("catch biz exception: " + ex.toString(), ex.getCause()); return this.asResponseEntity(HttpStatus.valueOf(ex.getHttpStatus()), ex.getErrorCode(), ex.getErrorMessage(), ex); } @ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class}) public ResponseEntity<Object> handleIllegalArgumentException(Exception ex) { logger.warn("catch illegal exception.", ex); return this.asResponseEntity(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.name().toLowerCase(), ex.getMessage(), ex); } @ExceptionHandler(Exception.class) public ResponseEntity<Object> handleException(Exception ex) { logger.error("catch exception.", ex); return this.asResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR.name().toLowerCase(), ExceptionConstants.INNER_SERVER_ERROR_MSG, ex); } protected ResponseEntity<Object> handleExceptionInternal( Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST); } logger.warn("catch uncustom exception.", ex); return this.asResponseEntity(status, status.name().toLowerCase(), ex.getMessage(), ex); } protected ResponseEntity<Object> asResponseEntity(HttpStatus status, String errorCode, String errorMessage, Exception ex) { Map<String, Object> data = new LinkedHashMap<>(); data.put(BizException.ERROR_CODE, errorCode); data.put(BizException.ERROR_MESSAGE, errorMessage); //是否包含异常的stack trace if(includeStackTrace){ addStackTrace(data, ex); } return new ResponseEntity<>(data, status); } private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) { StringWriter stackTrace = new StringWriter(); error.printStackTrace(new PrintWriter(stackTrace)); stackTrace.flush(); errorAttributes.put(BizException.ERROR_TRACE, stackTrace.toString()); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpssdz.html