SpringMVC全局异常处理
SpringMVC全局异常处理方式一般分为两大种:
- 使用 HandlerExceptionResolver 接口
- @ExceptionHandler
- @ResponseStatus
一、使用 HandlerExceptionResolver 接口
HandlerExceptionResolver 是org.springframework.web.servlet下的一个接口,可以解析在处理程序映射或执行期间抛出的异常,通常情况下会返回一个error views ,error views类似于JSP错误页面,但可以与任何类型的异常一起使用。
使用这个接口只需要实现resolveException方法(以下是API文档简单翻译了一下):
![这是API文档](https://i-blog.csdnimg.cn/blog_migrate/5a81b67d70605b5e6151c032b68a9d15.png)
具体示例(方法内可根据自己需求编写代码):
public class HandlerExceptionResolverDemo implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
if(e instanceof IOException){
return new ModelAndView("ioexp");
}else if(e instanceof SQLException){
return new ModelAndView("sqlexp");
}
return null;
}
也可以直接使用官方的实现类SimpleMappingExceptionResolver,当然无论是自己实现还是直接调用官方的都必须在spring的配置文件内配置(或者加注解)目的是为了能让spring管理:
<!-- 自己编写的简单异常处理器 -->
<bean class="com.expection.demo.HandlerExceptionResolverDemo "/>
<!--or下面的,自定义和官方提供的不冲突可同时使用-->
<!-- springmvc提供的简单异常处理器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面 -->
<property name="defaultErrorView" value="/WEB-INF/jsp/error.jsp"/>
<!-- 定义异常处理页面用来获取异常信息的变量名,也可不定义,默认名为exception -->
<property name="exceptionAttribute" value="ex"/>
<!-- 定义需要特殊处理的异常,这是重要点 -->
<property name="exceptionMappings">
<props>
<prop key="com.exception.demo.SuperExceptionResolver">/WEB-INF/jsp/custom_error.jsp</prop>
</props>
<!-- 还可以定义其他的自定义异常 -->
</property>
</bean>
从配置文件可以看出可以配置特殊处理的异常,也就是说可以自定义特殊异常类。
接下来可以自定义一些出异常的方法来测试一下
启动tomcat输入路径后就会发现页面跳转到了error.jsp。
从上面的过程可知,使用SimpleMappingExceptionResolver进行异常处理,具有集成简单、有良好的扩展性(可以任意增加自定义的异常和异常显示页面)、对已有代码没有入侵性等优点,但该方法仅能获取到异常信息,若在出现异常时,对需要获取除异常以外的数据的情况不适用。
二、@ExceptionHandler
API源码:
@Target(value=METHOD)
@Retention(value=RUNTIME)
@Documented
public @interface ExceptionHandler{
java.lang.Class<? extends java.lang.Throwable>[]
}
详解请见API文档(英文的)简单翻译一下(英语大神建议英文API):
用于处理特定处理程序类和/或处理程序方法中的异常的注释。在Servlet和Portlet环境之间提供一致的样式,语义适应具体环境。
使用此注释注释的处理程序方法允许具有非常灵活的签名。它们可以按任意顺序具有以下类型的参数:
- 异常参数:声明为一般异常或更具体的异常。如果注释本身不通过它缩小异常类型,这也可以作为映射提示value()。
请求和/或响应对象。您可以选择任何特定的请求/响应类型,例如 ServletRequest/ HttpServletRequest 或PortletRequest/ ActionRequest/ RenderRequest。 - 会话对象: HttpSession或者PortletSession。此类型的参数将强制存在相应的会话。因此,这样的论证永远不会null。 请注意,会话访问可能不是线程安全的,特别是在Servlet环境中:"synchronizeOnSession"如果允许多个请求同时访问会话,请考虑将标志切换 为“true”。
- WebRequest或 NativeWebRequest。允许通用请求参数访问以及请求/会话属性访问,而不与本机Servlet / Portlet API绑定。
- Locale对于当前请求区域设置
- InputStream/ Reader用于访问请求的内容。
- OutputStream/ Writer用于生成响应的内容。
- Model作为从处理程序方法返回模型映射的替代方法。
处理程序方法支持以下返回类型:
- ModelAndView对象
- Model对象
- Map对象
- View对象
- String对象
- @ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void
翻译的惨不忍睹,强烈建议看API文档
用法:在方法上加 @ExceptionHandler(value={}) ,value可以写一个或多个异常类码,如 @ExceptionHandler(value={IOException.class,SQLException.class}) ,若不写则默认所有异常。
示例:
@RequestMapping("/errorTest")
public class ErrorController {
@RequestMapping("/demo")
@ExceptionHandler(value={IOException.class,SQLException.class})
public void demo() {
//-------------方法内容-----------------
}
}
注意:
如果单使用@ExceptionHandler,只能在当前Controller中处理异常,因此往往都会配合@ControllerAdvice使用,就可以摆脱那个限制了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。
当然无论单独使用@ExceptionHandler还是使用@controlleradvice + @ ExceptionHandler 配合都必须确保处理类能被sping捕获到
优缺点:
- 优点:将 Controller 层的异常和数据校验的异常进行统一处理,减少模板代码,减少编码量,提升扩展性和可维护性。
- 缺点:只能处理 Controller 层未捕获(往外抛)的异常,对于 Interceptor(拦截器)层的异常,Spring 框架层的异常,就无能为力了。
三、好像还有第三种@ResponseStatus???
@ResponseStatus(value= ,reason=" ")
它有两个属性,value属性是http状态码,比如404,500等。reason是错误信息
示例:
//页面出现404后就会有一个友好的提示了
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "页面走丢了")
public class demoExceptionResolver extends RuntimeException {
//HttpStatus.NOT_FOUN-->404
//---------------------------------
}
来在API的警告:
警告:在异常类上使用此批注时,或者在设置reason此批注的属性时,HttpServletResponse.sendError将使用该方法。
因此HttpServletResponse.sendError,响应被认为是完整的,不应再写入。此外,Servlet容器通常会编写HTML错误页面,因此使用reason不适合的REST API。对于这种情况,最好使用a ResponseEntity作为返回类型并避免@ResponseStatus完全使用。
写的有点错略,有机会分别解释一下。