正文
Spring 统一异常处理有 3 种方式,分别为:
- 使用 @ ExceptionHandler 注解
- 实现 HandlerExceptionResolver 接口
- 使用 @controlleradvice 注解
使用 @ ExceptionHandler 注解
使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。使用如下:
1 @Controller 2 public class GlobalController { 3 4 /** 5 * 用于处理异常的 6 * @return 7 */ 8 @ExceptionHandler({MyException.class}) 9 public String exception(MyException e) { 10 System.out.println(e.getMessage()); 11 e.printStackTrace(); 12 return "exception"; 13 } 14 15 @RequestMapping("test") 16 public void test() { 17 throw new MyException("出错了!"); 18 } 19 }
可以看到,这种方式最大的缺陷就是不能全局控制异常。每个类都要写一遍。
实现 HandlerExceptionResolver 接口
这种方式可以进行全局的异常控制。例如:
1 @Component 2 public class ExceptionTest implements HandlerExceptionResolver{ 3 4 /** 5 * TODO 简单描述该方法的实现功能(可选). 6 * @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception) 7 */ 8 public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, 9 Exception ex) { 10 System.out.println("This is exception handler method!"); 11 return null; 12 } 13 }
使用 @ControllerAdvice 注解
上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。
那么,在实际中,就可以使用 @controlleradvice + @ ExceptionHandler。
1 @ControllerAdvice 2 @ResponseBody 3 public class WebExceptionHandle { 4 private static Logger logger = LoggerFactory.getLogger(WebExceptionHandle.class); 5 /** 6 * 400 - Bad Request 7 */ 8 @ResponseStatus(HttpStatus.BAD_REQUEST) 9 @ExceptionHandler(HttpMessageNotReadableException.class) 10 public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { 11 logger.error("参数解析失败", e); 12 return ServiceResponseHandle.failed("could_not_read_json"); 13 } 14 15 /** 16 * 405 - Method Not Allowed 17 */ 18 @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) 19 @ExceptionHandler(HttpRequestMethodNotSupportedException.class) 20 public ServiceResponse handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { 21 logger.error("不支持当前请求方法", e); 22 return ServiceResponseHandle.failed("request_method_not_supported"); 23 } 24 25 /** 26 * 415 - Unsupported Media Type 27 */ 28 @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) 29 @ExceptionHandler(HttpMediaTypeNotSupportedException.class) 30 public ServiceResponse handleHttpMediaTypeNotSupportedException(Exception e) { 31 logger.error("不支持当前媒体类型", e); 32 return ServiceResponseHandle.failed("content_type_not_supported"); 33 } 34 35 /** 36 * 500 - Internal Server Error 37 */ 38 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 39 @ExceptionHandler(Exception.class) 40 public ServiceResponse handleException(Exception e) { 41 if (e instanceof BusinessException){ 42 return ServiceResponseHandle.failed("BUSINESS_ERROR", e.getMessage()); 43 } 44 45 logger.error("服务运行异常", e); 46 e.printStackTrace(); 47 return ServiceResponseHandle.failed("server_error"); 48 } 49 }
继承 ResponseEntityExceptionHandler 类来实现针对 Rest 接口 的全局异常捕获,并且可以返回自定义格式:
1 @Slf4j 2 @ControllerAdvice 3 public class ExceptionHandlerBean extends ResponseEntityExceptionHandler { 4 5 /** 6 * 数据找不到异常 7 * @param ex 8 * @param request 9 * @return 10 * @throws IOException 11 */ 12 @ExceptionHandler({DataNotFoundException.class}) 13 public ResponseEntity<Object> handleDataNotFoundException(RuntimeException ex, WebRequest request) throws IOException { 14 return getResponseEntity(ex,request,ReturnStatusCode.DataNotFoundException); 15 } 16 17 /** 18 * 根据各种异常构建 ResponseEntity 实体. 服务于以上各种异常 19 * @param ex 20 * @param request 21 * @param specificException 22 * @return 23 */ 24 private ResponseEntity<Object> getResponseEntity(RuntimeException ex, WebRequest request, ReturnStatusCode specificException) { 25 26 ReturnTemplate returnTemplate = new ReturnTemplate(); 27 returnTemplate.setStatusCode(specificException); 28 returnTemplate.setErrorMsg(ex.getMessage()); 29 30 return handleExceptionInternal(ex, returnTemplate, 31 new HttpHeaders(), HttpStatus.OK, request); 32 } 33 34 }