欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring mvc 异常处理机制和统一异常处理

程序员文章站 2022-04-30 20:17:55
...

一、异常处理机制

Spring MVC 是通过 HandlerExceptionResolver 处理程序的异常,包括请求映射、数据绑定以及处理器执行时发生的异常

1、HandlerExceptionResolver

HandlerExceptionResolver 只有一个接口方法

public interface HandlerExceptionResolver {

    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

resolveException 方法尝试解决在处理程序执行期间引发的异常,处理之后返回 ModelAndView,转到对应的视图作为异常报告反馈给用户

下面是可用的 HandlerExceptionResolver 实现:

HandlerExceptionResolver 描述
SimpleMappingExceptionResolver 异常类名称和错误视图名称之间的映射,用于在浏览器应用程序中呈现错误页面
DefaultHandlerExceptionResolver 解决Spring MVC引发的异常,并将它们映射到HTTP状态代码
ResponseStatusExceptionResolver 使用@ResponseStatus注解解析异常,并根据注解中的值将它们映射到 HTTP 状态代码
ExceptionHandlerExceptionResolver 通过调用或类中的 @ExceptionHandler 方法来解决异常

Spring MVC 默认注册了这些内置异常解析器,用于支持 @ResponseStatus 注释异常和@ExceptionHandler方法

2、异常解析链

spring mvc 异常处理类结构图
spring mvc 异常处理机制和统一异常处理

上图我们可以看到 AbstractHandlerExceptionResolver 实现了 Ordered 接口,这样形成了一个异常处理链,DispatcherServlet 把异常委托给 HandlerExceptionResolver 解析链以解决异常并提供替代处理。这个与视图解析器是相似的。

可以通过HandlerExceptionResolver 在Spring配置中声明多个bean并根据需要设置 order 属性来形成异常解析链。order 的值越高,异常解析器定位的越晚。

HandlerExceptionResolver 有三种返回情况:

  • 处理后返回 ModelAndView 指向错误视图

  • 如果异常在解析器中已经处理完,则返回空的 ModelAndView

  • 返回 null,表明异常仍未解决,则由后续解析器继续处理,如果异常最后还在,则允许冒泡到Servlet容器

二、异常处理

  • 直接实现 HandlerExceptionResolver
  • 使用@ExceptionHandler注解
  • @aaa@qq.com

1、使用@ExceptionHandler注解

@ExceptionHandler 方法是在 @Controller 和 @ControllerAdvice 类中定义的,用来处理来自控制器方法的异常

如果 @ExceptionHandler 方法是在控制器内部定义的,那么它会接收并处理由控制器(或其任何子类)中的 @RequestMapping 方法抛出的异常;如果是定义在 @ControllerAdvice 类中,那么它会处理相关控制器中抛出的异常

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}

@ExceptionHandler方法的方法参数和返回值可以很灵活。比如,在Servlet环境下方法可以接收HttpServletRequest参数。返回值可以是String类型(会被解析为视图名)、可以是ModelAndView类型的对象,也可以是ResponseEntity

@Controller
public class SimpleController {

    @ExceptionHandler(RuntimeException.class)
    public ModelAndView error(RuntimeException error, HttpServletRequest request) {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("error");
        mav.addObject("param", "Runtime error");
        return mav;
    }

    @ExceptionHandler()
    public ModelAndView error(Exception error, HttpServletRequest request, HttpServletResponse response) {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("error");
        mav.addObject("param", "Exception error");
        return mav;
    }
}

@ExceptionHandler 只能处理同一个 Controller 里的异常,想要设置全局的异常处理,可把 @ExceptionHandler 方法写在一个 BaseController 里面,让其他 Controller 继承它。另外还可以使用 @ControllerAdvice+@ExceptionHandler

2、@aaa@qq.com

这里我们使用 @RestControllerAdvice ,相当于 @ControllerAdvice@ResponseBody

同时处理 web 和 ajax 异常

@RestControllerAdvice
public class ExceptionHandle {

    @ExceptionHandler
    public Object handle(HttpServletRequest request, HttpServletResponse response,Exception e){
        if(request.getHeader("X-Requested-With")!=null
            &&"XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString())){
            return JSONResult.error(e.getMessage());
        }else{
            ModelAndView mv = new ModelAndView();
            mv.addObject("exception",e);
            mv.addObject("url",request.getRequestURL());
            mv.setViewName("/error");
            return mv;
        }
    }
}

三、容器错误页面

如果异常最后未被任何 HandlerExceptionResolver 解决,并且因此将其传播或者如果响应状态设置为错误状态(即4xx,5xx),则Servlet容器可以在HTML中呈现默认错误页面。可以在 web.xml 中声明错误页面映射

web.xml

<error-page>
    <location>/error</location>
</error-page>
<error-page>
   <error-code>404</error-code>
   <location>/resources/error/404.html</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/resources/error/500.html</location>
</error-page>