目录
1、DefaultHandlerExceptionResolver——根据状态码配置
2、SimpleMappingExceptionResolver——根据异常类型配置
3、AnnotationMethodHandlerExceptionResolver —— 注解配置异常
我们知道,系统中异常包括:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。
一、异常处理思路
在springmvc中,异常处理的思路
上图所示,系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。
springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。明白了springmvc中的异常处理机制,下面就开始分析springmvc中的异常处理。
二、异常处理结构体系
Spring MVC通过HandlerExceptionResolver处理程序的异常,包括处理映射,数据绑定及处理器执行时发生异常。HandlerExceptionResolver仅有一个接口方法:
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
}
当发生异常时,Spring MVC将调用 resolveException()方法,并转到ModelAndView对应视图中,作为一个异常报告页面,反馈给用户!
HandlerExceptionResolver拥有几个以下常见实现类:
三、异常处理方案
1、DefaultHandlerExceptionResolver——根据状态码配置
Spring MVC默认装配了DefaultHandlerExceptionResolver,它会将Spring MVC框架的异常转换为相应的相应状态码!
自定义404 html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>404,资源找不到了!</h1>
</body>
</html>
在springmvc.xml中放开指定的静态资源拦截
静态资源注意会被DispatcherServlet拦截!
<mvc:resources location="/static/" mapping="/static/**"/>
在web.xml响应状态码配置一个对应页面
<error-page>
<error-code>404</error-code>
<location>/static/404.html</location>
</error-page>
执行结果:
在Controller中并没有test404这个方法,在没有设置自定义页面之前,报错信息如下
在设置自定义页面之后,跳转到自定义页面
2、SimpleMappingExceptionResolver——根据异常类型配置
如果希望对所有的异常进行统一的处理,比如当指定的异常发生时,把它映射到要显示的错误的网页中,此时用SimpleMappingExceptionResolver进行解析。
DispatcherServlet中没有实现SimpleMappingExceptionResolver的Bean,所有需要在springmvc的配置文件中进行配置。
示例如下:
Controller方法:抛出数组下标越界异常
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver() {
String[] values = new String[10];
// 下标越界了
System.out.println(values[11]);
return "success";
}
发送Http请求,控制器捕获请求后处理控制器逻辑,由于在逻辑中, 数组越界,会抛出ArrayIndexOutOfBoundsException异常。
配置使用SimpleMappingExceptionResolver来映射异常
<!--注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置使用SimpleMappingExceptionResolver来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 给异常命名一个别名 -->
<property name="exceptionAttribute" value="exception"></property>
<property name="exceptionMappings">
<props>
<!-- 一定要异常的全类名。 表示出现 ArrayIndexOutOfBoundsException异常,就跳转到error.jsp视图 -->
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
另外在/WEB-INF/jsp下新建一个error.jsp视图。因为上面配置的InternalResourceViewResolver视图解析器默认把error字符串解析为error.jsp视图。
error.jsp内容为:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF- 8">
<title>Insert title here</title></head>
<body>
<h1>Error Page</h1>
${requestScope.exception}
</body>
</html>
下面发送Http请求后,控制器截获请求并处理请求时,数组越界抛出一个ArrayIndexOutOfBoundsException一个异常,此时由SimpleMappingExceptionResolver异常解析!
3、AnnotationMethodHandlerExceptionResolver —— 注解配置异常
Spring MVC 默认注册了 AnnotationMethodHandlerExceptionResolver,它允许通过@ExceptionHandler注解指定处理特定异常的方法!
// 使用注解捕获指定异常
@ExceptionHandler(value = {RuntimeException.class})
public ModelAndView handleArithmeticException2(Exception exception) {
System.out.println("[出异常了]:" + exception);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", exception);
return mv;
}
// 使用注解捕获指定异常
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception exception) {
System.out.println("出异常了,算术异常:" + exception);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", exception);
return mv;
}
// 测试异常的方法
@RequestMapping("/testExceptionHandler")
public String test2() {
int i = 100 / 0;
return "success";
}
目标方法内抛出了一个ArithmeticException异常,将由继承关系最近的异常处理捕捉到,即由handleArithmeticException捕捉到。 若将handleArithmeticException方法注释掉,则发生ArithmeticException异常将由handleArithmeticException2进行处理。
缺点:
使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。
不能全局控制异常。每个类都要写一遍。
4、@ControllerAdvice——注解全局异常处理
上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个 Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个controller 中了。
这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。
也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。 请确保此WebExceptionHandle 类能被扫描到并装载进 Spring 容器中。
/**
* @author swadian
* @date 2021/2/7
* @Version 1.0
* @describetion
*/
@Controller
@ControllerAdvice
public class WebExceptionHandle {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex) {
System.out.println("全局异常:ex = " + ex);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("exception", ex);
return modelAndView;
}
}
开启注解扫描
<!--扫描控制层的包-->
<context:component-scan base-package="com.test.controller"/>
<!--注解驱动 -->
<mvc:annotation-driven/>
若在其他的由@Controller标记的Handler类中的Handle方法抛出异常,且没有在Handler类中定义@ExceptionHandler方法,则会去由@ControllerAdvice标记的类中去找,若也找不到,则在页面抛出异常。