type
status
date
slug
summary
tags
category
icon
password

一、DispatcherServlet 介绍

Spring MVC 的基本概念参考这篇文章 Imspring 源码介绍(四):WebMVC 实现,DispatcherServlet 在 Spring MVC 处理请求的整个流程中充当中转站的角色,调用多个组件来处理请求。DispatcherServlet 通过 SPI 机制来加载默认提供的相关组件,而 SPI 的核心就在于 DispathcerServlet.properties 文件。
notion image
这个文件定义了各类组件的默认实现。其中需要重点关注的是 HandlerMappingHandlerAdapterViewResolver 这三个组件。这三个组件在 DispatcherServlet 的处理流程中发挥核心作用。整个 DispatcherServlet 的处理流程可以简化为两步:
  1. 映射建立。ApplicationContext 初始化时建立所有 url 和 controller 类的对应关系,对应关系保存在 HandlerMapping 里面。
  1. 查找映射并处理请求。根据请求 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 的类继承体系,让大家对整体有一个简单的了解。
notion image
  • 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,他的原始参数只有 requestresponse,但是每个 Handler 的入参和返回结果可能都不同。HandlerAdapter 的作用就是把 requestresponse 解析成不同 Handler 的入参,然后把 Handler 的返回结果统一封装成 ModelAndView。
  • 第 5 步:这一步是真正去执行 controller 里面的方法。这里调用的是 AnnotationMethodHandlerAdapterhandle().handle() 方法,核心逻辑由 invokeHandlerMethod(request, response, handler) 实现。
这一部分的核心就是第 2 步和第 4 步。
  • 第 2 步:通过 request 找 controller 的处理方法。实际上就是拼接 controller 的 url 和方法的 url,与 request 的 url 进行匹配,找到匹配的方法。
  • 第 4 步是真正地执行方法。这一步通过 resolveHandlerArguments 解析方法参数,然后通过反射调用方法。

参考

Spring源码:AOP原理Spring MVC:Interceptor拦截器
mcbilla
mcbilla
一个普通的干饭人🍚
Announcement
type
status
date
slug
summary
tags
category
icon
password
🎉欢迎来到飙戈的博客🎉
-- 感谢您的支持 ---
👏欢迎学习交流👏