SpringMvc源码解析
最近公司这边在考虑Api gateway,准备用zuul来实现,借此机会,把SpringMvc又了解了下
SpringMvc我相信大家都有用过,但SpringMvc的整个执行流程,不知道大家是否了解。今天主要是针对SpringMvc的执行流程及原理和大家做个分享。
首先,我们可以通过一张图来了解SpringMvc的执行流程
任何一个框架,都有自己特定的适用领域,框架的设计和实现,必定是为了应付该领域内许多通用的,烦琐的、基础的工作而生。SpringMvc作为一个表现层框架,也必须直面Web开发领域中表现层中的几大点:
1、URL到框架的映射
2、http请求参数绑定
3、http响应的生成和输出
那SpringMvc到底是怎么完成以上所述的问题的呢?
下面我以一个Http请求流程,依次介绍SpringMvc整个执行流程,及相关核心接口与类:
用户在浏览器中输入http://www.xxx.com/abc/1的地址,回车后,浏览器发起一个http请求。请求到达服务器后,首先会被SpringMvc注册在web.xml中的前端转发器DispatcherServlet接收,DispatcherServlet是一个标准的servlet,它的作用是接受和转发web请求到内部框架处理单元
我们先看一下DispatcherServlet,通过doService->doDispath()方法:
a、通过HttpServletRequest获取一个HandlerMapping实现类
1、HandlerMaping
接下来,我们看下HandlerMapping接口
该接口只有唯一的一个方法,通过httpServletRequest获取一个HandlerExecutionChain
回到DispatcherServlet的执行流程,当DispatcherServlet接收到web请求后,由servlet处理doGet或doPost方法,最终转发到DispathcherServlet->doService方法(Spring在启动时,会加载程序注入进去的所有HandlerMapping实现类)。以该web请求的HttpServletRequest对象为参数,依次调用其getHandler方法,第一个不为null的调用结果,将被返回。DispatcherServlet类中获取handlerMapping方法如下:
到这,SpringMvc的第一步处理就完成了,一个Web请求经过处理后,会得到一个HandlerExecutionChain对象,这就是SpringMvc对URI映射的结果。HandlerMapping接口的getHandler方法参数是HttpServletRequest,这意味着,HandlerMapping的实现类可以利用HttpServletRequest中的 所有信息来做出这个HandlerExecutionChain对象的生成”决策“。这包括,请求头、url路径、cookie、session、参数等等一切你从一个web请求中可以得到的任何东西(最常用的是url路径)。
HandlerMapping返回的是一个HandlerExecutionChain类,从名字上看是对执行链的封装,接下来,我们看下HandlerExecutionChain定义
类较长,我只截取了参数列表,其实这里面最主要的也就是以下两行:
一个实质的执行对象,还有一个拦截器列表
HandlerInterceptor定义如下:
至此,HandlerExecutionChain整个执行脉络也就清楚了:在真正调用其handler对象前,HandlerInterceptor接口实现类组成的数组将会被遍历,其preHandle方法会被依次调用,然后真正的handler对象将被调用。handler对象被调用后,就生成了需要的响应数据,在将处理结果写到HttpServletResponse对象之前(SpringMVC称为渲染视图),其postHandle方法会被依次调用。视图渲染完成后,最后afterCompletion方法会被依次调用,整个web请求的处理过程就结束了。
在一个处理对象执行之前,之后利用拦截器做文章,这已经成为一种经典的框架设计套路。Struts2中的拦截器会做诸如参数绑定这类复杂的工作,那么SpringMVC的拦截器具体做些什么呢?我们暂且不关心,虽然这是很重要的细节,但细节毕竟是细节,我们先来理解更重要的东西。
HandlerInterceptor,可以看出这是SpringMvc的一个暴露点,通过自定义拦截器,我们可以在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情。
这个HandlerExecutionChain类中以Object引用所声明的handler对象,到底是个什么东东?它是怎么被调用的?
在理解该问题之前,我们先看一下另一个核心类HandlerAdapter:
在DispatcherServlet中,除了HandlerMappingg列表,也有HandlerAdapter列表,如:
HandlerExecutionChain中的handler对象会被作为参数传递进去,在DispatcherServlet类中注册的HandlerAdapter实现类列表会被遍历,然后返回第一个supports方法返回true的HandlerAdapter对象,用这个HandlerAdapter实现类中的handle方法处理handler对象,并返回ModelAndView这个包含了视图和数据的对象。HandlerAdapter就是SpringMVC提供的另一个扩展点,你可以提供自己的实现类来处理handler对象。
接下来,我们看下HandlerAdapter到底做了哪些事
进入到RequestMappingHandlerAdapter,找到invokeHandlerMethod方法,如下:
代码中红框框起来的地方是指:
1、根据handlerMethod和binderFactory创建一个ServletInvocableHandlerMethod。后续把请求直接交给ServletInvocableHandlerMethod执行。
2、createRequestMappingMethod方法比较简单,把之前RequestMappingHandlerAdapter初始化的argumentResolvers和returnValueHandlers添加至ServletInvocableHandlerMethod中
启动时,SpringMvc加载RequestMappingHandlerAdapter时,进初始化对应的参数转换器:
刚刚看到invokeHandlerMethod方法,在创建ServletInvocableHandlerMethod,后调用invokeAndHandle方法
这里,我们主要看下invokeForRequest,从方法名可以看出,通过反射调用对应的HandleMethod对应的method方法
获取参数中, 我们可以点进去看到,这里通过不同的参数注解,获取到对应的实现类,来获取参数信息
这里提个问题,如果是@RequestBody 和 @ResponseBody,SpringMvc又是怎么处理的呢?
有兴趣的朋友,可以接着深入看看HttpMessageConverter
HandlerAdapter处理完成,接着我们回到DispatcherServlet-doDispathcer()方法:
接着执行我们的拦截器 postHandle方法
HandlerAdapter返回的ModleAndView对象,我们看下View接口定义
所有的数据,最后会作为一个Map对象传递到View实现类中的render方法,调用这个render方法,就完成了视图到响应的渲染。这个View实现类,就是来自HandlerAdapter中的handle方法的返回结果。当然从ModelAndView到真正的View实现类有一个解析的过程,ModelAndView中可以有真正的视图对象,也可以只是有一个视图的名字,SpringMVC会负责将视图名称解析为真正的视图对象。
至此,我们了解了一个典型的完整的web请求在SpringMVC中的处理过程和其中涉及到的核心类和接口。
在一个典型的SpringMVC调用中,HandlerExecutionChain中封装handler对象就是用@Controller注解标识的类的一个实例,根据类级别和方法级别的@RequestMapping注解,由默认注册的DefaultAnnotationHandlerMapping(3.1.3中更新为RequestMappingHandlerMapping类,但是为了向后兼容,DefaultAnnotationHandlerMapping也可以使用)生成HandlerExecutionChain对象,再由AnnotationMethodHandlerAdapter(3.1.3中更新为RequestMappingHandlerAdapter类,但是为了向后兼容,AnnotationMethodHandlerAdapter也可以使用)来执行这个HandlerExecutionChain对象,生成最终的ModelAndView对象后,再由具体的View对象的render方法渲染视图。