spring mvc 异常处理机制和统一异常处理
一、异常处理机制
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 异常处理类结构图
上图我们可以看到 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>