1、控制器级别@ExceptionHandler
public class FooController{ //... @ExceptionHandler({ CustomException1.class, CustomException2.class }) public void handleException() { // } }
作用:只针对@Controller起作用。
弊端:
A、每个@Controller类都要写标记@ExceptionHandler的方法,当然可以统一写Base Controller,并将其写入。但,有些@Controller继承自其他BaseController,而该BaseController在其他jar包中,无法修改。
B、不能对整个应用起作用。
2、HandlerExceptionResolver
1)、DefaultHandlerExceptionResolver
作用:将标准Spring异常解析为对应的HTTP状态码,如404,500等。参见:spring exception完整列表
弊端:没有对response的body设置内容。虽然可通过ModelAndView自行写错误内容,但不是最佳的。
2)、ResponseStatusExceptionResolver
@ResponseStatus(value = HttpStatus.NOT_FOUND) public class MyResourceNotFoundException extends RuntimeException { public MyResourceNotFoundException() { super(); } public MyResourceNotFoundException(String message, Throwable cause) { super(message, cause); } public MyResourceNotFoundException(String message) { super(message); } public MyResourceNotFoundException(Throwable cause) { super(cause); } }
作用:使用自定义异常类,并在其上加注解@ResponseStatus,将这些异常映射到HTTP状态码。
弊端:与DefaultHandlerExceptionResolver一样,却是将状态码映射到response上,但body仍为null。
3)、自定义HandlerExceptionResolver
例子:如果客户端要求输入application / json,则在出现错误情况时,我们要确保我们返回一个以application / json编码的响应正文
@Component public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver { @Override protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { if (ex instanceof IllegalArgumentException) { return handleIllegalArgument( (IllegalArgumentException) ex, response, handler); } ... } catch (Exception handlerException) { logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException); } return null; } private ModelAndView handleIllegalArgument(IllegalArgumentException ex, HttpServletResponse response) throws IOException { response.sendError(HttpServletResponse.SC_CONFLICT); String accept = request.getHeader(HttpHeaders.ACCEPT); ... return new ModelAndView(); } }
作用:自定义异常
弊端:返回的依旧是ModelAndView,在这里为response body设置内容。
3、@ControllerAdvice
@ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class }) protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) { String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); } }
作用:@ControllerAdvice为支持全局的@ExceptionHandler,该机制有别于旧的MVC模型,利用ResponseEntity和@@ExceptionHandler带来了便利。
A、能够设置response的body和状态码。
B、能将多个异常映射到同一方法,以便一起处理。
C、充分利用了RESTful ResponseEntity来响应。
4、ResponseStatusException(适用于spring5及以上)
@GetMapping(value = "/{id}") public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) { try { Foo resourceById = RestPreconditions.checkFound(service.findOne(id)); eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response)); return resourceById; } catch (MyResourceNotFoundException exc) { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Foo Not Found", exc); } }
作用:
A、一种类型(ResponseStatusException),多种状态码。与@ExceptionHandler相比,减少了耦合。
B、我们将不用创建那么多自定义异常类。
C、由于可通过编程方式创建异常,因此可以更好的控制异常处理。
弊端:
A、没有统一、全局的异常处理方式:对比应用程序范围的全局异常(@ControllerAdvice)来说,提供全局异常很难。
B、代码复制:在多个Controller类中复制代码。
那如何解决以上问题呢?
全局异常:@ControllerAdvice
局部异常:ResponseStatusException
但是,如果发生了相同的异常时,是重复执行还是报错?