文章目录
1:为什么要学习DispatcherServlet的九大组件
在如下的这篇文章中
从一个请求入口来带你探究DispatcherServlet的奥秘——SpringMVC的核心组件——万字长文
已经探讨过了,为什么一个请求最终会到DispatcherServlet中的doDispatch方法中实现和该方法中的执行可以分为11个逻辑代码块。那为什么今天要聊这个话题呢?
这些组件对我们今后的开发是否有帮助呢?以我之愚见,我觉得是有的,举一个例子:九大组件之中的处理器异常解析器组件,如果我们去探究HandlerExceptionResolver 的源码,在以后的开发中我们就可以自定义实现 HandlerExceptionResolver 处理异常(可以作为默认的全局异常处理规则)等等。
那这个九大组件和doDispatch方法又有什么关系呢?在上面的文章中的末尾我们已经提到
doDispatch方法中出现了很多组件,这些组件都是Spring MVC整个处理过程中不可获缺的部分。正是通过这些组件之间的搭配组合,才令整个Spring MVC框架完整地运行起来,使用框架进行开发时,各种便捷功能都是通过这些组件辅助完成的。这就像盖房子一样,通过一个个小组件,最终搭建起一个完整的艺术品。
2:处理器异常解析器介绍
在请求处理的过程中,如果发生任何异常,在最终执行处理结果方法procesHandlerException 时,会尝试把该异常解析为异常的ModelAndView结果,后续使用相同逻辑处理此ModelAndView。
从代码层面看上面的话,如下图
处理器异常解析器(HandlerExceptionResolver )只有一个方法,即
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
共有传入请求、响应、查找到的处理器、发生的异常4个参数,返回对该请求解析后的ModelAndView异常视图。
默认情况下,包括两个异常解析器: DefaultErrorAttributes 与HandlerExceptionResolver-Composite。第一个仅用于保存异常相关信息到请求属性中,并没有其他实际作用。第二个则组合了3个异常解析器,在该异常解析器的解析方法中,遍历内部的3个异常解析器,返回第一个非空的异常ModelAndView结果。
那在代码调试中怎么找到这两个异常解析器呢?我们在这先给一个调试截图,关于代码调试会在后面一一讲述
如上图可知3个异常解析器按顺序分别为ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver。
这3个类型都继承了AbstractHandlerExceptionResolver抽象类。在抽象类中,添加Ordered 接口用于支持指定异常解析器的顺序。同时其实现了HanderExceptionResolver 接口的resolveException方法,封装些异常解析时的统一操作,代码如下:
AbstractHandlerExceptionResolver类下的resolveException代码逻辑
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
//调用本类的shouldApplyTo方法,并判断此异常解析器是否对该请求和处理器提供支持
if (shouldApplyTo(request, handler)) {
//预处理响应,如在某些情况下对于错误结果不需要缓存,就在该方法中进行处理
prepareResponse(ex, response);
//执行真正的解析异常方法,由子类实现
ModelAndView result = doResolveException(request, response, handler, ex);
//结果不为空,返回结果
if (result != null) {
//..
logException(ex, request);
}
return result;
}
else {
return null;
}
}
下面的3,4,5章就为大家分别介绍ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver的实现和源码调试,第6章就是利用前面学习到的知识,自己定义一个HandlerExceptionResolver 来处理异常
3:ExceptionHandlerExceptionResolver
ExceptionHandler异常解析器用于支持通过注解@ExcptionHandler配置的异常处理方法。同时该类型通过重写shouldApplyTo,对处理器类型进行判断,只有类型为HandlerMethod的处理器产生的异常才视为支持,即只解析@RequestMapping注解相关逻辑中产生的异常。
千万不要小看这一段话,这一段话包含很多重要知识点,接下来为大家一一道来。
对于一个应用来说,正常情况还会发生各种意外情况。比如处理过程中,框架抛出各种异常,在处理器方法执行过程中抛出异常,发生异常情况时该如何处理呢?Spring MVC提供了处理器异常解析器用于处理执行过程中发生的各种异常。
在提供了整体的基于注解的开发方式后,异常处理器的逻辑也相应的提供了基于注解的方式进行声明。注解@ExceptionHandler就是用于声明异常处理方法,而ExceptionHandlerExceptionResolver就是对应于注解的异常解析器。同时这种解析器,仅针对HandlerMethod类型的处理器才适用,即这个处理器异常解析器组件整体与RequstMappingHandlerMapping和RequestMappingHandlerAdapter进行成套服务。
对于初学者来说,这两段话会听起来有点懵,不过在接下来的过程中,作者会尽量的使用源码与知识点结合的方式带大家搞明白
下面就来分析该异常解析器的详细逻辑,学会如何实现使用简单的注解声明就能达到的异常处理机制。
3.1 @ExceptionHandler 解析器概述
与@RequestMapping注解标记请求处理器方法类似,@ExceptionHandler 注解用于标记异常处理器方法。其整体特性与@RequestMapping相关特性基本相同。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
//注解中提供属性value,可以配置Class数组,指定发生哪些异常时使用该异常处理器方法执行
Class<? extends Throwable>[] value() default {
};
}
注解中提供属性value,可以配置Class数组,指定发生哪些异常时使用该异常处理器方法执行,这与@RequestMapping的查找请求对应的处理器方法类似。
异常处理器方法享有与请求处理器方法相同的参数解析与返回值处理功能,这些功能的实现都是依赖于@ExceptionHandler对应的异常解析器ExceptionHandlerExceptionResolver。这个解析器在功能上与RequestMappingHandlerAdapter 的功能类似,最终返回的结果同样是ModelAndView类型,用于提供发生异常时需要返回的ModelAndView结果。
所以可以把@ExceptionHandler标记的方法理解为@RequestMapping相关逻辑执行过程中发生异常时的替代方法。接下来就是要说和这个注解相关的逻辑在ExceptionHandlerExceptionResolver中是如何实现的。
在第2章中已经了解异常解析器的相关知识,在执行异常解析器的解析异常方法resolveException时,在所有异常解析器的同一个抽 象类AbstractHandlerExceptionResolver中,提供了shouldApplyTo方法,只有这个返回为true 时,才执行真正的解析异常方法doResolveException。在ExceptionHandlerExceptionResolver中,shouldApplyTo方法由其父类AbstractHandlerMethodExceptionResolver实现,
上面的一段话出现了很多类和方法,可能我们已经眼花缭乱,这就是源码的精髓所在,在后面我们来实现全局异常的示例会带大家调试一遍源码,可能那样就会好多了吧。
AbstractHandlerMethodExceptionResolver类下的shouldApplyTo方法逻辑实现如下
/**是否可以被该异常解析器处理
@param request 原始的请求
@param handler 处理器查逻辑中找到的处理器,可以为空,如果在执行查找前发生异常就一定为空
*/
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
//处理器如果为空,则交给父类判断
if (handler == null) {
//默认该逻辑返回true
return super.shouldApplyTo(request, null);
}
else if (handler instanceof HandlerMethod) {