一、Spring MVC 的异常处理流程
Spring MVC 中通过HandlerExceptionResolver处理程序的异常,包括Handler映射数据绑定以及木币方法执行时发生的异常
Spring MVC 提供了HandlerExceptionResolver的实现类
默认提供了三个HandlerExceptionResolver
在controller包中新增一个HandlerExceptionController
@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()方法中增加可能会出现异常的代码
@RequestMapping("/handler") public String handler(Integer x){ int res = 10 / x; System.out.println(x); return "success"; } 复制代码
在HandlerExceptionController中增加异常处理方法,使用@ExceptionHandler注解指定能处理的异常类型
// 专门处理异常的方法,指定类型 @ExceptionHandler(Exception.class) public String handlerExceptionAlpha(){ System.out.println("handlerExceptionAlpha()方法运行了"); // 返回自定义的错误页面 return "error"; } 复制代码
在pages目录下新增一个错误页面error.jsp
<%@ 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中,再从页面中取出
// 专门处理异常的方法,指定类型 // 直接返回ModelAndView,将异常信息方法封装在类中 @ExceptionHandler(Exception.class) public ModelAndView handlerException(Exception e){ System.out.println("handlerException()方法运行了"); ModelAndView mv = new ModelAndView("error"); mv.addObject("e", e); return mv; } 复制代码
在页面中取出异常信息
<%@ 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()方法
@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; } } 复制代码