1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何完成异常解析、捕捉异常,并自定义异常和异常解析器

2.源码分析

进入DispatcherServlet的processDispatchResult方法

1024行判断异常是否是ModelAndViewDefiningException类型,如果是,直接返回ModelAndView

不是ModelAndViewDefiningException类型,则获取HandlerMethod,调用processHandlerExeception方法

点进去1030行的processHandlerException方法,该方法根据HandlerExecutionResolvers来解析异常并选择ModelAndView

1217行遍历HandlerExceptionResolvers,我们讲过,在<mvc:annotation-driven/>帮我们注册了默认的异常解析器

请看AnnotationDrivenBeanDefinitionParser(解析annotation-driven的类)

1218行调用HandlerExceptionResolver的resolveException方法,该方法被子类AbstractHandlerExceptionResolver实现

1225行给request设置异常信息

现在进入HandlerExceptionResolver接口resolveException方法的实现处——AbstractHandlerExceptionResolver的resolveException方法

131行判断该异常解析器是否可以被应用到Handler

135行为异常情况准备response,即给response添加头部

136行调用抽象方法doResolveException,由子类实现

进入AbstractHandlerMethodExceptionResolver的doResolveException方法

59行调用抽象方法,被子类ExceptionHandlerExceptionResolver实现

打开ExceptionHandlerExceptionResolver的doResolveHandlerMethodException方法

362行获取有异常的Controller方法

367~368行为ServletInvocableHandlerMethod设置HandlerMethodArgumentResolverComposite和HandlerMethodReturnValueComposite,用来解析参数和处理返回值

380行调用invokeAndHandle方法处理返回值,暴露cause

384行无cause

3.实例

3.1 使用@ResponseStatus自定义异常UnauthorizedException

@ResponseStatus会被ResponseStatusExceptionResolver解析

@ResponseStatus(code=HttpStatus.UNAUTHORIZED,reason="用户未授权")
public class UnauthorizedException extends RuntimeException { }

测试方法

    @RequestMapping("/unauth")
public Map unauth() {
throw new UnauthorizedException();
}

浏览器输入http://localhost:8080/springmvcdemo/error/unauth

3.2 无注解情况

测试方法

    @RequestMapping("/noSuchMethod")
public Map noHandleMethod() throws NoSuchMethodException {
throw new NoSuchMethodException();
}

没有@ExceptionHandler和@ResponseStatus注解则会被DefaultHandlerExceptionResolver解析

浏览器输入http://localhost:8080/springmvcdemo/error/noSuchMethod

3.3 @ExceptionHandler处理异常

测试方法

@ExceptionHandler会被ExceptionHandlerExceptionResolver解析

    @RequestMapping("/exception")
@ResponseBody
public Map exception() throws ClassNotFoundException {
throw new ClassNotFoundException("class not found");
} @RequestMapping("/nullpointer")
@ResponseBody
public Map nullpointer() {
Map resultMap = new HashMap();
String str = null;
str.length();
resultMap.put("strNullError",str);
return resultMap;
} @ExceptionHandler(RuntimeException.class)
@ResponseBody
public Map error(RuntimeException error, HttpServletRequest request) {
Map resultMap = new HashMap();
resultMap.put("param", "Runtime error");
return resultMap;
} @ExceptionHandler()
@ResponseBody
public Map error(Exception error, HttpServletRequest request, HttpServletResponse response) {
Map resultMap = new HashMap();
resultMap.put("param", "Exception error");
return resultMap;
}

浏览器输入http://localhost:8080/springmvcdemo/error/classNotFound

浏览器输入http://localhost:8080/springmvcdemo/error/nullpointer

根据异常类继承关系,ClassNotFoundException离Exception更近,所以被@ExceptionHandler()的error方法解析,注解无参相当于Exception.class。

同理,NullPointerException方法离NullPointerException“最近”,把@ExceptionHandler(NullPointerException.class)的error方法注释掉,浏览器输入http://localhost:8080/springmvcdemo/error/nullpointer,会发现

浏览器返回RuntimeException,印证了我们的说法

3.4 定义全局异常处理

/**
* @Author: 谷天乐
* @Date: 2019/1/21 10:48
* @Description: ExceptionHandlerMethodResolver内部找不到Controller的@ExceptionHandler注解的话,
* 会找@ControllerAdvice中的@ExceptionHandler注解方法
*/
@ControllerAdvice
public class ExceptionControllerAdvice { @ExceptionHandler(Throwable.class)
@ResponseBody
public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("error", error.getMessage());
map.put("result", "error");
return map;
} }

浏览器输入http://localhost:8080/springmvcdemo/error/unauth

优先级关系:@ExceptionHandler>@ControllerAdvice中的@ExceptionHandler>@ResponseStatus

要把TestErrorController中@ExceptionHandler的方法注释掉才会有效果

4.总结

HandlerExceptionResolver作为异常解析器的接口,核心方法是resolveException

AbstractHandlerExceptionResolver实现HandlerException,resolveException方法内部调用抽象方法doResolveException,该方法被子类实现;shouldApplyTo方法检查该异常解析器是否可以被应用到Handler

AbstractHandlerMethodExceptionResolver的doResolveException内部调用抽象方法doResolveHandlerMethodException,由子类实现,返回ModelAndView,可以在视图模型里自定义错误页面;shouldApplyTo调用父类方法

ExceptionHandlerExceptionResovler的doResolveHandlerMethodException处理异常,返回ModelAndView

DefaultHandlerExceptionResolver的doResolveException处理默认异常

ResponseStatusExceptionResolver的doResolveException方法处理@ResponseStatus修饰的异常

DispatcherServlet的processHandlerException方法根据注册的HandlerExceptionResolvers选择一个ModelAndView

DispatcherServlet的doDispatch方法调用processDispatchResult,该方法处理Handler的选择和调用的结果,processDispatchResult方法调用processHandlerException

5.参考

https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conversion

https://docs.spring.io/spring/docs/current/javadoc-api/

https://github.com/spring-projects/spring-framework

文中难免有不足,欢迎指正

SpringMVC源码阅读:异常解析器的更多相关文章

  1. SpringMVC源码阅读:拦截器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  2. SpringMVC源码阅读:视图解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  3. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  4. SpringMVC源码阅读:过滤器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  5. SpringMVC源码阅读:核心分发器DispatcherServlet

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...

  6. SpringMVC源码阅读:Controller中参数解析

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  7. SpringMVC源码阅读:定位Controller

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...

  8. SpringMVC源码阅读:Json,Xml自动转换

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  9. SpringMVC源码阅读:属性编辑器、数据绑定

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

随机推荐

  1. golang array, slice, string笔记

    本来想写一篇关于golang io的笔记,但是在学习io之前必须了解array, slice, string概念,因此将在下篇写golang io.   array: 数组的长度是该数组类型的一部分, ...

  2. QTP之回放模式(ReplayType)

    QTP的回放模式有两种,如下所示: 1.  Event模式  --  事件跟踪 2.  Mouse模式 --   鼠标跟踪 Event模式就是我们平时默认用的模式,也就是事件,其实QTP的click方 ...

  3. [翻译]NUnit---TestCase Attributes(二十一)

    TestCaseAttribute (NUnit 2.5) TestCase特性有两个效果,包括标记一个方法使用参数并且在调用的时候提供内置数据.示例如下,本示例会使用不同数据集执行3次: [Test ...

  4. .net加密web.config文件

    这篇文章我将介绍如何利用ASP.NET来加密和解密Web.config中连接字符串,之前只写了第二种方式,现在将两种方式都写出来,供大家参考.不管使用哪种方式我们在程序中都不需要写任何代码来解密连接字 ...

  5. Day44 数据库的操作

    视图操作: 1.左连接查询 select * from person left join dept on person.dept_id = dept.did 2. 右连接 3. 内连接  inner ...

  6. FFMpeg的一些基础配置

    一 . CMakeLists.txt文件的使用 1.添加头文件的相对路径 : include_directories(include(这里面就是文件的名字)) 2.设置ffmpeg的库的路径(v7a或 ...

  7. 雨天的尾巴(bzoj3307)(线段树合并+树上差分)

    \(N\)个点,形成一个树状结构.有\(M\)次发放,每次选择两个点\(x,y\) 对于\(x\)到\(y\)的路径上(含\(x,y\))每个点发一袋\(Z\)类型的物品.完成 所有发放后,每个点存放 ...

  8. SQL语句常见优化十大案例

    1.慢SQL消耗了70%~90%的数据库CPU资源: 2.SQL语句独立于程序设计逻辑,相对于对程序源代码的优化,对SQL语句的优化在时间成本和风险上的代价都很低:3.SQL语句可以有不同的写法: 1 ...

  9. Django(wsgiref、jinja2模块使用介绍)

    day60 wsgiref比较稳定 """ 根据URL中不同的路径返回不同的内容--函数进阶版 返回HTML页面 让网页动态起来 wsgiref模块版 "&qu ...

  10. elasticsearch 5.x Delete By Query API(根据条件删除)

    之前在 2.X版本里 这个Delete By Query功能被去掉了 因为官方认为会引发一些错误 如需使用 需要自己安装插件. bin/plugin install delete-by-query 需 ...