SpringMVC源码阅读:异常解析器
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/current/javadoc-api/
https://github.com/spring-projects/spring-framework
文中难免有不足,欢迎指正
SpringMVC源码阅读:异常解析器的更多相关文章
- SpringMVC源码阅读:拦截器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:视图解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读系列汇总
1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...
- SpringMVC源码阅读:过滤器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:核心分发器DispatcherServlet
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...
- SpringMVC源码阅读:Controller中参数解析
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:定位Controller
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...
- SpringMVC源码阅读:Json,Xml自动转换
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:属性编辑器、数据绑定
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
随机推荐
- Easy Ui 的reload 问题
当我删除某条数据时,删除成功后要刷新datagrid 这时调用reload方法就不成功,而要用下面的方式. 正确代码$('#fixedGrid').datagrid("reload" ...
- LeetCode150:Evaluate Reverse Polish Notation
题目: Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are + ...
- 如何连接MSSQLSERVER2014(其他版本数据库大体相同)
购买了服务器后我在上面安装了SqlServer2014,但是程序中总是远程连接不上,特此记录一下: 一.一些基本设置如下链接: 转载自:https://blog.sun0816.com/20381.h ...
- .net core 与ELK(4)后台运行els可视化工具和Kibana
which nohup .bash_profile中并source加载 如果没有就安装吧 yum provides */nohup nohup npm run start & nohup ./ ...
- 浏览器对HTTP请求的编码行为
浏览器对请求的URL编码行为 浏览器会对请求的URL中非ASCII码字符进行编码.这里不是指对整个URL进行编码,而是仅仅对非ASCII码字符部分进行编码,详情请看下面的实验记录. 实验一:在URL参 ...
- C语言作业03-函数
1.本章学习总结 1.1 思维导图 1.2本章学习体会,代码量学习体会 1.2.1学习体会 通过这几周的函数学习,让我明白了函数的重要性,在很多时候运用函数,会使得代码分工明确,逻辑严密,不繁琐.函数 ...
- Django分页设置
1. """ 分页组件使用示例: obj = Pagination(request.GET.get('page',1),len(USER_LIST),request.pa ...
- Android---------------Service的学习
一.创建与启动Servcie的三个步骤 : 1. 创建一个类并继承Servcie 2.在配置文件中注册服务 3.通过Intent来启动服务 二.Service的两种启动方式 1.startServce ...
- SAX,功能强大的 API
https://www.ibm.com/developerworks/cn/xml/x-saxapi/ SAX,功能强大的 API 在摘自 XML by Example 的本篇预览中比较了 DOM 和 ...
- HTML 5 使用 FileReader、FormData实现文件上传
一.FileReader FileReader 对象允许Web应用程序异步读取存储在用户计算机中的文件(或缓冲区的原始数据),使用File或Blob对象指定要读取的文件或数据. 1.1 实例化 var ...