type
status
date
slug
summary
tags
category
icon
password
一、DispatcherServlet 介绍
Spring MVC 的基本概念参考这篇文章 Imspring 源码介绍(四):WebMVC 实现,DispatcherServlet 在 Spring MVC 处理请求的整个流程中充当中转站的角色,调用多个组件来处理请求。DispatcherServlet 通过 SPI 机制来加载默认提供的相关组件,而 SPI 的核心就在于
DispathcerServlet.properties
文件。这个文件定义了各类组件的默认实现。其中需要重点关注的是
HandlerMapping
、HandlerAdapter
和 ViewResolver
这三个组件。这三个组件在 DispatcherServlet 的处理流程中发挥核心作用。整个 DispatcherServlet 的处理流程可以简化为两步:- 映射建立。ApplicationContext 初始化时建立所有 url 和 controller 类的对应关系,对应关系保存在
HandlerMapping
里面。
- 查找映射并处理请求。根据请求 url 找到对应的 controller,并使用 controller 处理请求,并返回结果视图。这里从
HandlerMapping
取出 url 对应的 controller,并封装成HandlerAdapter
进行实际处理,拿到处理结果后使用ViewResolver
解析处理结果并返回结果视图。
下面是更具体的实现,对于使用注解
@Controller
的控制器类:HandlerMapping
的实现类是RequestMappingHandlerMapping
。这是 Spring MVC 流程的核心类,它的工作流程一般是这样的:- 应用启动阶段:
RequestMappingHandlerMapping
实现了接口 InitializingBean,它的初始化方法会设置各种工作参数,并检测容器中所有的控制器方法并将它们登记管理起来。针对每个使用注解 @RequestMapping 的控制器方法所生成的请求匹配条件是一个 RequestMappingInfo 对象,最终所被管理的是一个 HandlerMethod 对象。 - 应用运行时:当一个请求到达时,
RequestMappingHandlerMapping
会被 DispatcherServlet 用于匹配一个 HandlerMethod 并最终调用它处理该请求。匹配的依据是当前请求中的路径信息和 RequestMappingHandlerMapping 所管理的每个控制器方法的 RequestMappingInfo 请求匹配条件。
HandlerAdapter
的实现类是RequestMappingHandlerAdapter
。它负责拿到RequestMappingHandlerMapping
返回的方法后,进行一系列处理后,调用目标方法处理请求,这一系列处理包括:数据绑定和数据校验,返回值处理等等。
下面的源码主要围绕这两个实现类展开。
二、DispatcherServlet 代码流程
RequestMappingHandlerMapping 类结构
在正式讲解流程前,先分析下
RequestMappingHandlerMapping
的类继承体系,让大家对整体有一个简单的了解。AbstractHandlerMapping
:提供基础设施支持,例如: 路径解析、拦截器、跨域。 规定了根据 request 得到 handler 的模板方法处理流程getHandler,具体如何根据 request 寻找到某个 handler,则是由子类实现。
AbstractHandlerMethodMapping
:囊括了对注解 Controller 寻找,建立映射和根据 request 找到对应 handler 的流程支持,核心在于建立 Reuqest 和 HandlerMethod 的映射关系,将识别处理器方法和建立映射的任务交给子类实现。
RequestMappingHandlerMapping
: 核心在于解析处理器方法和对应 Controller 上 @RequestMapping 注解,然后合并生成一个RequestMappingInfo 作为映射的关键一环返回。
1、映射建立
下面开始正式进入源码。首先
RequestMappingHandlerMapping
和 RequestMappingHandlerAdapter
是在配置类 WebMvcConfigurationSupport
类里面通过 @Bean
注解引入到容器里面的。从上面的继承图可以看到
RequestMappingHandlerMapping
的父类 AbstractHandlerMethodMapping
实现了 InitializingBean
接口,在 Spring 启动的过程中会执行 afterPropertiesSet
方法,Reuqest 和 HandlerMethod 的映射关系就是在这一步创建的。initHandlerMethods
是映射建立的入口,我们需要深入其中processCandidateBean
是核心方法,主要是检测指定 bean
类是否使用了注解 @Controller
或者 @RequestMapping
,如果使用了,则认为这是目标类,会调用方法 detectHandlerMethods
从中检测控制器方法。detectHandlerMethods
的作用是探测每个控制器方法上的 RequestMappingInfo
信息并注册登记,这里会调用一个 getMappingForMethod
来构建 RequestMappingInfo
对象,这是我们真正要存起来的对象。getMappingForMethod
用于从控制器类的一个方法上尝试根据类级别和方法级别的 @RequestMapping
信息构建相应的 RequestMappingInfo
对象。经过以上这些逻辑,开发人员定义的所有控制器方法都被注册登记到
RequestMappingHandlerMapping
之中了,这些信息供随后 DispatcherServlet
在处理用户请求时使用。2、查找映射并处理请求
第二个步骤是由请求触发的,所以入口为
DispatcherServlet
的核心方法为 doService()
,doService()
中的核心逻辑由 doDispatch()
实现,我们查看 doDispatch()
的源代码。这里关心几个核心步骤
- 第 2 步:
getHandler(processedRequest)
方法实际上就是从HandlerMapping中找到 url 和 controller 的对应关系。这也就是第一个步骤建立Map<url,Controller>的意义。
- 第 3 步:
getHandlerAdapter(mappedHandler.getHandler())
获取真正处理请求的 HandlerAdapter。之所以需要 HandlerAdapter 这个角色,是因为DispatcherServlet
作为一个 servlet,他的原始参数只有request
和response
,但是每个 Handler 的入参和返回结果可能都不同。HandlerAdapter 的作用就是把request
和response
解析成不同 Handler 的入参,然后把 Handler 的返回结果统一封装成 ModelAndView。
- 第 5 步:这一步是真正去执行 controller 里面的方法。这里调用的是
AnnotationMethodHandlerAdapter
的handle().handle()
方法,核心逻辑由invokeHandlerMethod(request, response, handler)
实现。
这一部分的核心就是第 2 步和第 4 步。
- 第 2 步:通过 request 找 controller 的处理方法。实际上就是拼接 controller 的 url 和方法的 url,与 request 的 url 进行匹配,找到匹配的方法。
- 第 4 步是真正地执行方法。这一步通过
resolveHandlerArguments
解析方法参数,然后通过反射调用方法。
参考
- Author:mcbilla
- URL:http://mcbilla.com/article/670e7329-2571-44d0-8cac-2f241aa329e0
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts