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 ...
随机推荐
- 微信Web APP应用
微信Web APP即微信公众账号,对web APP的提供者来说这是一个门槛极低,容易到达数亿真实用户且确保用户黏性的分发平台;对用户来说,这是一种前所未有及其简单的应用使用方式;对腾讯来 说,将形成微 ...
- C# 基础篇
全篇依据 C#高级编程(第9版) 内容记录: 基础知识C# 5.0 基础 分为15章内容来介绍 核心C# 对象和类型 继承 泛型 数组 运算符和类型强制转换 委托和lambda表达式,事件 字符串和正 ...
- XML文件之创建
1.创建XML文档对象XmlDocument doc=new XmlDocument() 2.创建XML根节点变量XmlElement xmlElement 3.判断XML文件是否已经存在 1)若存在 ...
- DevExpress WinForms Controls 学习系列1
一个偶然的机会,接触到DevExpress.项目是WinForm的,应用了DevExpress.为了使项目具备维护开发的生命力,我觉得有必要系统的学习一下DevExpress,今天是学习笔记的第一篇. ...
- 如何做好iOS应用安全?这有一把行之有效的“三板斧”
本文由 网易云发布. iOS应用面临很多破解问题,常见的有IAP内购破解.山寨版本.破解版本等:大众应用上,微信抢红包.微信多开等:而在iOS游戏上,越来越泛滥的外挂问题也不断困扰着游戏厂商. 网易 ...
- .NET Core中使用Dapper操作Oracle存储过程最佳实践
为什么说是最佳实践呢?因为在实际开发中踩坑了,而且发现网上大多数文章给出的解决方法都不能很好地解决问题.尤其是在获取类型为OracleDbType.RefCursor,输出为:ParameterDir ...
- Android的四大组件学习
一.Linearlayout : 线性布局 1. android:orientation="vertical" //控件的方向控制,vertical : 垂直布局 , ...
- class字节码结构(一)(字节码结构和字节常量池的结构)
<Java虚拟机原理图解> 1.1.class文件基本组织结构 关于变量的几个叫法: 局部变量/全局变量:很好区分根据所在位置. 类变量:静态的全局变量. 类常量:全局的final修饰的变 ...
- C# 后缀名
用Visual Studio进行开发时项目内会有很多文件,其中常见的文件名后缀及其功能如下: .sln:解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需的信息. .csproj:项目文 ...
- mysql优化器在统计全表扫描的代价时的方法
innodb 的聚集索引 的叶子结点 存放的 是 索引值以及数据页的偏移量 那么在计算全表扫描的代价是怎么计算的呢? 我们知道代价 为 cpu代价+io代价 cpu代价 就是 每5条记录比对 计算一个 ...