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. System.Windows.Freezable 在未被引用的程序集中定义

    System.Windows.Freezable 在未被引用的程序集中定义 解决方法 添加windowsbase.dll 引用

  2. netty--NioEventLoop滴干活

    netty是最近项目要用到的nio框架,找了各种资料,发现称赞它的有点多,所以决定用它:其实也就二选一嘛,mina或netty或自己写.对于mina,也不熟,不过看各种介绍,貌似netty干活还是很不 ...

  3. LeetCode140:Word Break II

    题目: Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where e ...

  4. MPU6050学习笔记(电源管理器1、2)

    Technorati Tags: 嵌入式开发.模块开发 寄存器107号:电源管理器1 PWR_MGMT_1: Description: This register allows the user to ...

  5. The Beam Model:Stream & Tables翻译(上)

    作者:周思华 欢迎访问网易云社区,了解更多网易技术产品运营经验. 本文尝试描述Beam模型和Stream & Table理论间的关系(前者描述于数据流模型论文.the-world-beyond ...

  6. SqlAlchemy操作(一)

    博客转载于 http://www.cnblogs.com/haiyan123/p/8270520.html 一. 介绍 SQLAlchemy是一个基于Python实现的ORM框架.该框架建立在 DB ...

  7. ThinkPHP5.0手把手实现手机阿里云短信验证

    阿里云短信服务介绍阿里云短信服务就是以前的阿里大于,不过现在融合得到阿里云平台了.首先,你需要注册一个阿里云账号,这个自行解决. 仅用于测试使用官方送的代金券够用了.相关配置1.开通阿里云Access ...

  8. 日志分析与splunk浅谈

    难易程度:★★★ 阅读点:linux;python;web安全;日志分析; 文章作者:xiaoye 文章来源:i春秋 关键字:网络渗透技术 前言 linux下的日志分析对企业来说非常重要,对我们分析p ...

  9. 大白dmeo (转的)

    <!doctype html><html> <head> <meta charset="utf-8"> <title>B ...

  10. iOS —— GCD 详解

    一.什么是GCD Grand Central Dispatch (强大的中枢调度器) ,是异步执行任务的技术之一.纯C语言,有很多强大的函数. 二.GCD的优势 (1)GCD是苹果公司为多核并行运算提 ...