一、Spring MVC 的异常处理流程
Spring MVC 中通过HandlerExceptionResolver处理程序的异常,包括Handler映射数据绑定以及木币方法执行时发生的异常
Spring MVC 提供了HandlerExceptionResolver的实现类
默认提供了三个HandlerExceptionResolver
在controller包中新增一个HandlerExceptionController
代码语言:javascript复制@Controller
public class HandlerExceptionController {
@RequestMapping("/handler")
public String handler(Integer x){
int res = 10 / x;
System.out.println(x);
return "success";
}
}
启动应用,在浏览器输入http://localhost:8080/handler?x=0
这个报错页面是由Tomcat提供的,并不是Spring MVC提供的。
在DispatcherServlet的doDispatch()方法的1067行打上断点,开启Debug模式,在浏览器输入http://localhost:8080/handler?x=0
1067行代码就是执行目标方法,并且此时dispatchException=null,此时没有异常,待目标方法执行后就是出现异常,点击Step Over进入异常处理
目标方法中的异常出现,并赋值各个dispatchException,继续点击Step Over,并Step Into 到processDispatchResult方法中
此时异常不为空,并且异常类型不是if条件中的异常类型所以会直接进入esle代码块中,继续Step Over 到processHandlerException方法执行的这一行,并且Step Into 到processHandlerException方法中,该方法返回一个ModelAndView类
进入Step Over,进入到for循环中
此时就出现了前面说的Spring MVC 默认配置的三个HandlerExceptionResolver,在这个for循环中3个异常解析器会逐个解析 by zero这个异常,继续Step Over
多次点击Step Over,可以确定默认配置的三个异常解析器都无法解析 by zero 这个异常,也就是说Spring MVC最终不会返回任何的页面,我们看到的页面是Tomcat提供的错误页面
Spring MVC 默认配置的三个异常解析器的使用场景
- ExceptionHandlerExceptionResolver:解析@ExceptionHandler注解标注的异常
- ResponseStatusExceptionResolver:解析@ResponseStatus注解标注的异常
- DEfaultHandlerExceptionResolver:判断是否是Spring MVC自带的异常
二、ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver异常处理器用于处理@ExceptionHandler注解指定的异常,在HandlerExceptionController中handler()方法中增加可能会出现异常的代码
代码语言:javascript复制@RequestMapping("/handler")
public String handler(Integer x){
int res = 10 / x;
System.out.println(x);
return "success";
}
在HandlerExceptionController中增加异常处理方法,使用@ExceptionHandler注解指定能处理的异常类型
代码语言:javascript复制// 专门处理异常的方法,指定类型
@ExceptionHandler(Exception.class)
public String handlerExceptionAlpha(){
System.out.println("handlerExceptionAlpha()方法运行了");
// 返回自定义的错误页面
return "error";
}
在pages目录下新增一个错误页面error.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是自定义的错误页面</h2>
</body>
</html>
重新启动应用,浏览器输入 localhost:8080/handler?x=0
能够返回自定义的页面,但是没有显示异常信息。想要获取异常信息可以在方法中返回ModelAndView,将错误信息放在ModelAndView中,再从页面中取出
代码语言:javascript复制// 专门处理异常的方法,指定类型
// 直接返回ModelAndView,将异常信息方法封装在类中
@ExceptionHandler(Exception.class)
public ModelAndView handlerException(Exception e){
System.out.println("handlerException()方法运行了");
ModelAndView mv = new ModelAndView("error");
mv.addObject("e", e);
return mv;
}
在页面中取出异常信息
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>这是自定义的错误页面</h2>
<h3>错误信息为以下内容</h3>
<p>${e}</p>
</body>
</html>
重新启动应用,再次输入 localhost:8080/handler?x=0
页面中成功显示出异常信息
全局处理异常
显然在Controller类中书写异常处理方法很不优雅,可以新建一个exception包,新建一个GlobalExceptionResolver全局异常处理类,该类需要添加一个@ControllerAdvice类,告诉Spring MVC这是一个异常处理类,将HandlerExceptionController中的异常处理方法移到该类中,HandlerExceptionController中只保留handler()方法
代码语言:javascript复制@ControllerAdvice
public class GlobalExceptionResolver {
// 专门处理异常的方法,指定类型
// 直接返回ModelAndView,将异常信息方法封装到对象中
@ExceptionHandler(Exception.class)
public ModelAndView handlerException(Exception e){
System.out.println("全局handlerException()方法运行了");
ModelAndView mv = new ModelAndView("error");
mv.addObject("e", e);
return mv;
}
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handlerArithmeticException(Exception e){
System.out.println("全局handlerArithmeticException()方法运行了");
ModelAndView mv = new ModelAndView("error");
mv.addObject("e", e);
return mv;
}
}
再次启动应用
控制台输出是由lhandlerArithmeticException处理的这个异常
如果有多个异常的情况下可以写多个异常处理的方法,指定处理的异常可以是具体的异常也可以是Exception类,当同时出现时精确匹配优先
在HandlerExceptionController中增异常处理方法,处理Exception类异常
代码语言:javascript复制// 专门处理异常的方法,指定类型
// 直接返回ModelAndView,将异常信息方法封装到对象中
@ExceptionHandler(Exception.class)
public ModelAndView handlerException(Exception e){
System.out.println(this.getClass().getName() "类中的handlerException()方法运行了");
ModelAndView mv = new ModelAndView("error");
mv.addObject("e", e);
return mv;
}
重新启动该应用
根据控制台的输出,可以确定不管是否是精确匹配,优先使用同一个类下的异常处理方法来处理异常
三、ResponseStatusExceptionResolver
如果想要处理自定义的异常,则需要用到@ResponseStatus注解来标注,该注解不能标在方法上。在handler()方法上标注@ResponseStatus注解,看看会发生什么
这会导致正常页面也出现报错
该注解需要标在自定义异常类上,HandlerExceptionController中新增一个方法handlerAlpha()
代码语言:javascript复制@RequestMapping("/alpha")
public String handlerAlpha(@RequestParam("username") String username){
if (!"admin".equals(username)){
System.out.println("不是Admin,登录失败");
throw new NonAdminException();
}
return "success";
}
定义一个异常类,当非管理员登录时抛出该异常
代码语言:javascript复制@ResponseStatus(value = HttpStatus.CONFLICT, reason = "不是管理员不能登录,走吧走吧.....")
public class NonAdminException extends RuntimeException {
}
重启应用,当username为admin时,输出sucess页面
不是admin时,输出了指定的错误页面,并输出了异常信息
根据控制台的输出,可以确定该异常是被同一类下的异常处理方法处理的;注释HandlerExceptionController中的异常处理方法,将GlobalEXception中的Exception异常处理也注释掉;再次重启,浏览器中输入http://localhost:8080/alpha?username=admin111
四、DefaultHandlerExceptionResolver
HandlerExceptionController中新增一个方法
代码语言:javascript复制@PostMapping(value = "/bravo")
public String handlerBravo(){
return "success";
}
该方法是POST方法,重启应用,浏览器中输入localhost:8080/bravo
页面中的这段异常信息就是Spring MVC自定义的异常中的信息。开启GlobalException中的Exception处理方法
当GlobalException中的Exception处理方法被注释掉时,就是默认的DefaultHandlerExceptionResolver进行的处理
启动DEBUG模式,点击首页的bravo超链接
进入循环异常处理器列表的代码块中
多次Step Over后,只有DefaultHandlerExceptionResolver,可以处理这类异常
Step Into 到resolveException()这个方法中
继续Step Over
进入这个doResolveException()方法中
这里就包含了请求方法不支持的异常,也就是我们出现的异常
五、SimpleMappingExceptionResolver
SimpleMappingExceptionResolver是通过配置来进行异常处理的,在Spring MVC 配置文件中配置这个异常处理器
代码语言:javascript复制<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.NullPointerException">error</prop>
</props>
</property>
</bean>
注释掉异常处理中的Exception异常处理方法,在HandlerExceptionController 新增一个方法模拟空指针异常
代码语言:javascript复制@RequestMapping("/charlie")
public String handleCharlie(){
System.out.println("NullPointException.....");
// 模拟空指针异常的情况
String name = null;
System.out.println(name.length());
return "success";
}
index页面增加超链接
代码语言:javascript复制<a href="/charlie">charlie</a>
重新启动应用,点击index页面的超链接
没有出现错误信息,需要将错误信息保存,并修改Spring MVC配置文件才可以
代码语言:javascript复制<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- exceptionMappings:配置哪些异常去哪些页面 -->
<property name="exceptionMappings">
<props>
<!-- key:异常全类名;value:要去的页面视图名; -->
<prop key="java.lang.NullPointerException">error</prop>
</props>
</property>
<!--指定错误信息取出时使用的key -->
<property name="exceptionAttribute" value="e"></property>
</bean>
error页面通过配置的e或者默认的exception来去除错误信息
{e} - {exception}
如果全局异常处理存在处理空指针的方法
会优先使用全局的异常处理来处理,如果全部不能处理,在使用配置的方式处理
开启Debug模式
现在有四个全局异常处理器,SimpleMapping排在最后,第一个异常处理器就可以将这类异常处理掉,处理完成之后就不会使用其余3个异常处理器处理异常了。