Spring3.1.0实现原理分析(十九).MVC异常处理
大家好,今天谈谈spring mvc的异常处理架构,应该说异常处理也是mvc框架的一个不可或缺的基本功能。那么在什么情况下会调用异常处理器呢,z在处理器处理请求出现异常的情况下会调用异常处理器执行善后事宜。用伪代码来表述下,基本上是这样的模型。try{处理请求} catch{ 调用异常处理器 }。
spring默认会注册三种异常处理器,按优先级从高到低分别是“ExceptionHandlerExceptionResolver”,“ResponseStatusExceptionResolver”,“DefaultHandlerExceptionResolver”。不过其中的ResponseStatusExceptionResolver并不太使用,相反另一个非默认需要用户手动注册的异常处理器“SimpleMappingExceptionResolver”反倒是经常使用的,所以本篇博客主要介绍“ExceptionHandlerExceptionResolver”,“DefaultHandlerExceptionResolver”,“SimpleMappingExceptionResolver”这三个异常处理器的实现原理。下图是异常处理模块的类结构图。
最顶层的HandlerExceptionResolver接口只定义了一个方法,就是处理异常对象。AbstractHandlerExceptionResolver实现了两个功能,第一它在形式上实现了HandlerExceptionResolver接口,但并没有真正实现异常处理逻辑,只是定义了一个处理异常的抽象方法留给派生类实现;其次它增加了一个判断方法,即在调用异常处理方法之前先判断该异常处理器是否匹配处理器,默认实现逻辑是异常处理器匹配任意处理器,但是派生类可以通过重写该方法实现自己的控制逻辑。AbstractHandlerMethodExceptionResolver限定了自己只支持HandlerMethod处理器。ExceptionHandlerExceptionResolver派生自ExceptionHandlerExceptionResolver,它是一个真正干活的异常处理器。同样地DefaultHandlerExceptionResolver,SimpleMappingExceptionResolver也是真正干活的异常处理器,它们支持任意处理器。
一. ExceptionHandlerExceptionResolver
这个异常处理器是局部的。当处理器抛出异常后,它根据抛出的异常类型去处理器中查找相应的方法来处理异常。即A处理器抛出异常,使用A处理器定义的方法来处理异常,所以说它是局部的。在处理器中被@ExceptionHandler注解的方法是异常处理方法,注解时可以指定异常类型,异常类型和处理方法形参对应关系。
ExceptionHandlerExceptionResolver实现了InitializingBean接口,目的是为了获得初始化参数解析器和返回值解析器的机会。这些解析器的作用是为了获取异常处理方法的实参值和处理方法返回值。关于参数解析器和返回值解析器可以详看之前的博客。
使用ExceptionHandlerExceptionResolver第一次调用异常处理方法时,它会先创建一个ExceptionHandlerMethodResolver对象,这个对象可以理解为基于@ExceptionHandler注解的异常处理方法解析器,在这个对象的构造函数中它会解析处理器中所有被@ExceptionHandler注解的方法列表,解析方法的@ExceptionHandler注解,提取异常类型和方法对象置入缓存,并且它还提供了根据异常类型获取处理方法的功能,如果某个异常类型本身没有对应的处理方法,则尝试根据其超类获取对应的处理方法。
二. DefaultHandlerExceptionResolver
这个类可以理解为自动全局异常处理器。它有这么几个特点,第一它是全局的,它可以处理任意处理器抛出的异常。第二它是自动的,不需要添加任何配合和代码,自动实现对异常的处理。第三它只处理部分异常对象,不过基本涵盖了服务器可能会发生的异常。举个例子,比如服务器抛出了“SimpleMappingExceptionResolver”异常,DefaultHandlerExceptionResolver为response对象设置应答码404,但是它不会设置视图名称,视图名称都是空,因此不会执行视图渲染。
三. SimpleMappingExceptionResolver
这个类也是全局异常处理器,它维护了异常类型和视图名称之间的对应关系。这种对应关系是用户配置的,除了能配置异常类型和视图名称的关系,还能配置视图名称和应答结果码的关系。比如,
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.web.servlet.PageNotFound">pageNotFound</prop>
</props>
</property>
<property name="statusCodes">
<map>
<entry key="pageNotFound" value="404" />
</map>
</property>
</bean>
SimpleMappingExceptionResolver为把这种对应关系存储在两个成员变量中,
/**
* 异常映射(key:异常对象名称;value:视图名称)
*/
private Properties exceptionMappings;
/**
* 状态码map(key:视图名称;value:结果码)
*/
private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
当根据异常类型获取视图名称时,如果当前异常类型没有对应的视图名称,会尝试根据异常的超类型去获取视图名称。
总结:
1. spring mvc的异常处理模块,只负责处理视图渲染之前发生的异常,如果是在视图渲染过程中发生的异常spring是不处理的。
2. 处理异常时,spring会先调用优先级最高的异常处理器,如果异常被处理了则返回,如果未处理则继续调用次高优先级的异常处理器。