Spring Boot「15」统一异常处理

悠悠 2023-09-23 12:49 202阅读 0赞

今天我们将一块学习下 Spring MVC 中实现统一异常处理的几种方式。

总得来说,统一异常处理有三种方式:

  1. @Controller + @ExceptionHandler
  2. ExceptionHandlerExceptionResolver
  3. @ControllerAdvice + @ExceptionHandler

接下来,我们逐个演示下上述三种方式。

01-在 Controller 类中使用@ExceptionHandler注解

其使用方式如下:

  1. @Controller
  2. public class SayHiController {
  3. @ExceptionHandler(value = {MyDemoException.class})
  4. @ResponseBody
  5. public String myDemoExceptionHandler() {
  6. return "MyDemoException Exception";
  7. }
  8. }
  9. 复制代码

@Controller中增加一个方法,其上通过注解@ExceptionHandler标明其要处理的异常类型。 当我们后续的请求在当前@Controller中抛出异常时,会首先使用 ExceptionHandler 来处理。 如果遇到 ExceptionHandler 处理不了的异常类型,则会抛出来,例如:

format_png

此种方式的显著缺点,无法在@Controller Bean 之间复用、共享,每个都需要单独地定义 ExceptionHandler。

02-使用 HandlerExceptionResolver

Spring Boot 程序在启动时,会注册两个 HandlerExceptionResolver bean 到容器中:DefaultErrorAttributes 和 HandlerExceptionResolverComposite。 其中后者是一个组合类,内部包含一个列表。 它自己并不处理 Exception,而是交由其内部的 resolvers 来处理 Exception。 其内部 resolver 包括(优先级依次降低):

  • ExceptionHandlerExceptionResolver
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver

02.1-ExceptionHandlerExceptionResolver

主要用来查找是否有合适的、标注了@ExceptionHandler的方法能够处理遇到的异常。

上节中介绍的@Controller+@ExceptionHandler方式就是通过 ExceptionHandlerExceptionResolver 实现的。 以及后面要介绍的@ControllerAdvice+@ExceptionHandler也是如此。

02.2-ResponseStatusExceptionResolver

主要是负责处理带有@ResponseStatus注解或继承自 ResponseStatusException 类的异常。

@ResponseStatus是Spring 3.0引入的,主要用来将某个自定义异常与 HTTP 状态码关联起来。 当 Spring MVC 处理请求的过程中遇到的异常类标注了@ResponseStatus注解,Spring 会自动将此异常处理,并向 Response 中添加对应的状态码。 例如,我们定义如下的异常:

  1. @ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE)
  2. public class MyDemoException extends RuntimeException{
  3. // ...
  4. }
  5. 复制代码

当处理请求过程中抛出了此类型的异常,Spring 会捕捉,并获取对应的 HTTP 状态码,将其放置到 Response 中:

  1. @GetMapping("/somecustomexception")
  2. @ResponseBody
  3. public String someOtherWithCustomException() throws MyDemoException {
  4. if (true) {
  5. throw new MyDemoException();
  6. }
  7. return "some ~~other~ runtime exception";
  8. }
  9. 复制代码

此种方式虽然可以实现异常类复用,但是仍然有许多不变之处。 必须定义许多自定义异常,而且异常一旦定义且与某个状态码绑定,那所有同类型异常都只能返回同一个状态码,不够灵活。

Spring 5.0 引入了 ResponseStatusException 异常,解决了上述不够灵活的问题。 上述事例中,我们可以通过 ResponseStatusException 异常来改写:

  1. @GetMapping("/someresponsestatusexception")
  2. @ResponseBody
  3. public String someOtherWithResponseStatusException() {
  4. if (true) {
  5. throw new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE);
  6. }
  7. return "ResponseStatusException";
  8. }
  9. 复制代码

02.3-DefaultHandlerExceptionResolver

主要是将标准的 Spring MVC 异常转换成对应的 HTTP 状态码。 具体的异常类型及其对应的 HTTP 状态码可以参考官方网站介绍Handling Standard Spring MVC Exceptions。

02.4-自定义 HandlerExceptionResolver

通过继承 AbstractHandlerExceptionResolver,我们可以定义自己的异常处理器。

  1. @Component
  2. public class MyDemoHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
  3. public MyDemoHandlerExceptionResolver() {
  4. this.setOrder(-1);
  5. }
  6. @Override
  7. protected ModelAndView doResolveException(
  8. HttpServletRequest request,
  9. HttpServletResponse response,
  10. Object handler,
  11. Exception ex) {
  12. if (ex instanceof RuntimeException) {
  13. try {
  14. response.sendError(HttpServletResponse.SC_CONFLICT);
  15. } catch (Exception e) {
  16. }
  17. }
  18. return new ModelAndView();
  19. }
  20. }
  21. 复制代码

AbstractHandlerExceptionResolver 实现了 Ordered 接口,因此我们可以通过控制 order 值来控制所有 Resolver 的优先级。

03-使用@ControllerAdvice实现全局异常处理

前两节中的方法都是 Spring 3.2 之前的方式,Spring 3.2 之后引入了更方便的方法,即@ControllerAdvice

  1. @ControllerAdvice
  2. public class MyDemoControllerAdvice extends ResponseEntityExceptionHandler {
  3. @ExceptionHandler(value = {MyDemoException.class})
  4. public ResponseEntity<Object> handle(RuntimeException ex, WebRequest request) {
  5. String bodyOfResponse = "This should be application specific";
  6. return handleExceptionInternal(ex, bodyOfResponse,
  7. new HttpHeaders(), HttpStatus.GONE, request);
  8. }
  9. }

发表评论

表情:
评论列表 (有 0 条评论,202人围观)

还没有评论,来说两句吧...

相关阅读