首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 . 前言
文章目标 :
- 梳理 SpringMVC 主流程中的相关逻辑
- 确定 相关流程得参数
本文章未涉及的部分:
- MVC 的配置
- 容器的初始化
- 容器的加载
- 等….
二 . 流程分析
此主要流程包括如下几个部分 :
- 注解的扫描处理
- 请求的拦截
- 请求的转换
- 请求的最终处理
2.1 注解的扫描处理
注解的扫描主要在 DispatcherServlet.initHandlerMappings 中 , 其逻辑处理类在 AbstractHandlerMethodMapping.getHandlerInternal 中.
2.1.1 注解的扫描
注解扫描的起点主要是 RequestMappingHandlerMapping , 其中做了这几件事 :
- 在 AbstractHandlerMethodMapping 中通过 InitializingBean 的 afterPropertiesSet 处理所有的方法
- detectHandlerMethods 中进行核心的处理
- 对 RequestMapping 进行了扫描操作 , 并且生成 RequestMappingInfo
1 | java复制代码 |
2.1.2 Bean 的注册
上面的环节已经对 Mapping Method 扫描完成 , 在此环节中进行注册 , 注册主要使用内部类MappingRegistry
, 其内部存在多个 Map ,用于保存地址的映射关系
(url -MappingRegistration/HandlerMethod/CorsConfiguration 等 )
1 | java复制代码 |
2.2 请求的来临
请求的入口还是通过 Servlet 来完成的 , 先看一下 Servlet 体系 PS:51_01
2.2.1 起点 : 初始化一个 Servlet
MVC 是基于 Servlet 的体系结构 , 其最初的起点是通过 StandardWrapper 进行 initServlet 开始
流转路径如下 :
1 | java复制代码StandardWrapper -> GenericServlet (init) -> HttpServletBean (init) -> FrameworkServlet (initServletBean) |
看一个很老的图 , 大概对 DispatcherServlet 有个初步的了解 :
2.2.2 核心 : DispatchServlet 的初始化
1 | java复制代码C51- DispatcherServlet |
Filter 处理逻辑 , Filter 分为 3步 : 初始化 , 拦截 , 销毁 , 此处主要是初始化操作 >>
Filter 不是 Spring 专属的对象 , 其归属于 Servlet 原生体系 , 主要有以下2个流程
子流程一 : 核心处理容器 : StandardContext,添加 Filter 流程
- ServletWebServerApplicationContext # selfInitialize : 获取所有的需要处理的 Filter 类
- ServletContextInitializerBeans # ServletContextInitializerBeans : 从 Factory 中获取具体的类
- RegistrationBean # onStartup : 启动 并且注册 FilterBean
- StandardContext # addFilterDef : 添加 FilterDef , 即 filter definition
1 | java复制代码 |
子流程二 : 调用 Filter 初始化流程
这是一个异步操作 : 通过一个 call 流程异步处理 , 具体的流程在 org.apache.catalina.core 中
- StandardContext # filterStart : Set filterDefs.entrySet 中获取所有的 Filter
- ApplicationFilterConfig : 构建一个 ApplicationFilterConfig 并且在其中调用 initFilter() 初始化
1 | java复制代码 |
PS:51_01 Servlet 家族体系
PS:M51_10_01 HandlerMapping 家族体系
2.3 请求的解析
servlet 的起始方法是 HttpServlet , Spring 部分的核心方法是实现类FrameworkServlet
解析前置处理操作
- 在 Reuqest 和 Response 转换为 HttpServletRequest 和 HttpServletResponse 后
- 调用 HttpServlet 中 service(HttpServletRequest req, HttpServletResponse resp) 方法 , 触发 FrameworkServlet doGet 方法
- 最终进入 DispatcherServlet 核心类 , 处理一个 Http 请求
2.3.1 DispatcherServlet 获取处理 Handler
1 | java复制代码Start -> HttpServlet |
PS:M51_02_01 WebAsyncManager 介绍
- 作用 : 用于管理异步请求处理的中心类,主要用作SPI,通常不被应用程序类直接使用
- 核心 : 通过多个不同的线程 , 处理请求和结果
- 流程
- 异步场景以线程(T1)中正常的请求处理开始
- 并发请求处理可以通过调用startCallableProcessing或startDeferredResultProcessing来启动,这两种方法都会在单独的线程(T2)中产生一个结果。
- 保存结果并将请求分发给容器,以便在第三个线程(T3)中继续处理保存的结果。
- 在已分派线程(T3)中,可以通过getConcurrentResult()访问保存的结果,或者通过hasConcurrentResult()检测其是否存在。
PS:M51_02_02 : checkMultipart 的目的
为什么这里需要校验一下 checkMultipart ? 其底层主要做了如下几件事 :
- 判断是否有 multipartResolver 并且用该解析器判断是否为Multipart
- 如果以上条件达成 , multipartResolver.resolveMultipart(request) 进行解析返回新 Request
- 否则返回原有 request
说白了 , 就是 multipart request 的处理 , 但是相对的问题又来了 >>> 什么是 multipart request ?
作用 : HTTP 多部分请求是 HTTP 客户机构造的 HTTP 请求,用于将文件和数据发送到 HTTP 服务器。
场景 : 浏览器和 HTTP 客户端通常使用它将文件上传到服务器。
PS:M51_02_04 getHandler 和 getHandlerAdapter 的区别 ?
1 | java复制代码getHandler -> HandlerExecutionChain |
PS:M51_02_05 GET/HEAD 请求细说
这地方老有意思了 ,刚接触代码那会这里出问题想了好久才知道有这么个东西
参考文档 @ www.cnblogs.com/longyongzhe…
特点 : HEAD方法跟GET方法相同,只不过服务器响应时不会返回消息体。一个HEAD请求的响应中,HTTP头中包含的元信息应该和一个GET请求的响应消息相同。这种方法可以用来获取请求中隐含的元信息,而不用传输实体本身。也经常用来测试超链接的有效性、可用性和最近的修改。
作用 :
- 只请求资源的首部;
- 检查超链接的有效性;
- 检查网页是否被修改;
- 多用于自动搜索机器人获取网页的标志信息,获取rss种子信息,或者传递安全认证信息等
PS:M51_02_09 前置处理和后置处理主要处理什么 ?
1 | java复制代码- mappedHandler.applyPreHandle : 前置处理 |
M51_02 : DispatcherServlet # doDispatch 源码
1 | java复制代码 |
2.3.2 HandlerMapping 解析到方法
上文中已经获取到了 Adapter , 以下就是 Adapter 的详细处理
流程处理
流程处理分为2个部分 , 简单点说就是 :
- getHandler 获取使用的 MethodHandler
- getHandlerAdapter 获取适配处理器
- 通过适配处理器处理 MethodHandler
一 : 获取处理的 Handler (getHandler)
DispatcherServlet # M51_03- getHandler 中可以看到 , 通过 HandlerExecutionChain # getHandler 获取 Handler 对象
1 | java复制代码C76- AbstractHandlerMapping |
M32_02 lookupHandlerMethod 请求处理
1 | java复制代码protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { |
步骤二 : 使用获取的 Handler (HandlerAdapter.handle)
从步骤一中我们已经获取到了 HandleMethod , 后续就是相关的处理逻辑
- DispatcherServlet # doDispatch : 请求入口
- AbstractHandlerMethodAdapter # handle : RequestMappingAdapter 的父入口
- RequestMappingHandlerAdapter # handleInternal :请求处理主流程
- RequestMappingHandlerAdapter # invokeHandlerMethod
- ServletInvocableHandlerMethod # invokeAndHandle
- InvocableHandlerMethod # invokeForRequest
- InvocableHandlerMethod # doInvoke
- Method # invoke : Java 反射到方法
步骤2-1 : RequestMappingHandlerAdapter # handleInternal 处理
在该方法中 , 如果需要视图解析,则调用准备ModelAndView的RequestMapping处理程序方法
核心 : ModelAndView 的准备
1 | java复制代码protected ModelAndView invokeHandlerMethod(HttpServletRequest request, |
ModelAndViewContainer 参数详情 :
1 | java复制代码 |
2.4 补充 : 属性的转换
属性转换的主要类是 RequestMappingHandlerAdapter , 其主要调用逻辑为 :
- C90- RequestMappingHandlerAdapter # M90_1- invokeHandlerMethod
- C91- ServletInvocableHandlerMethod # M91_1- invokeAndHandle
- C92- InvocableHandlerMethod # M92_1- invokeForRequest : 对请求进行处理
- C93- InvocableHandlerMethod # M93_1- getMethodArgumentValues : 获取参数
- C94- HandlerMethodArgumentResolverComposite # M94_1- resolveArgument : 解析参数
- C95- RequestResponseBodyMethodProcessor # M95_1- readWithMessageConverters : 转换参数
- C96- AbstractMessageConverterMethodArgumentResolver # M96_1- readWithMessageConverters : 最终处理逻辑
1 | java复制代码C- RequestMappingHandlerAdapter |
Step 2 : 参数的获取
1 | java复制代码 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, |
Step 3 : RequestResponseBodyMethodProcessor 解析 RequestBody
1 | java复制代码 |
Step end : 最终处理
最终处理中, 对 属性参数进行了最后的映射
PS: M96_1_01 继承体系
- C- EmptyBodyCheckingHttpInputMessage
- C- HttpInputMessage
- C- HttpMessage
PS:M96_01_02 , 所有的 MessageConverter
- ByteArrayHttpMessageConverter
- StringHttpMessageConverter
- ResourceHttpMessageConverter
- ResourceRegionHttpMessageConverter
- SourceHttpMessageConverter
- AllEncompassingFormHttpMessageConverter
- Jaxb2RootElementHttpMessageConverter
- MappingJackson2HttpMessageConverter
1 | java复制代码protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, |
PS:M96_01_02 参数详情
2.5 补充 : Response 的转换
属性的转换主要在 HandlerMethodReturnValueHandlerComposite 中进行 , 主要的逻辑为 :
- C- InvocableHandlerMethod # invokeForRequest
- C- ServletInvocableHandlerMethod # invokeAndHandle
- C- HandlerMethodReturnValueHandlerComposite # handleReturnValue : 调用 Handler 处理
- C- RequestResponseBodyMethodProcessor # handleReturnValue
1 | java复制代码// - C91- ServletInvocableHandlerMethod # M91_1- invokeAndHandle |
总结
总算是把 MVC 写完了 , 本来篇幅不大 ,结果越写越多 , 反而不容易说清楚了.
初始化阶段中
, RequestMappingHandlerMapping 调用 initHandlerMethods 完成对所有的 Bean 进行扫描 , 最后构建 RequestMappingInfo 再通过 MappingRegistry 进行注册
在调用的阶段
, 首先调用 DispatcherServlet , 它会调用 AbstractHandlerMapping 中 getHandler 获取 HandlerMapping ( lookupHandlerMethod 调用 MappingRegistry 进行匹配)
匹配完成后
, 会来到 Adapter 主逻辑 , RequestMappingHandlerAdapter 中进行 ModelAndView 等处理 , 同时调用 ServletInvocableHandlerMethod 进行方法的映射
在方法映射阶段
, 会来到 InvocableHandlerMethod 类 , 首先会进行参数的解析和映射 , 最后通过 AbstractMessageConverterMethodArgumentResolver 和 HttpMessageConverter 进行联合处理
映射完成后
, 就会 invoke 到对应的方法 , 同时通过 HandlerMethodReturnValueHandlerComposite 和 RequestResponseBodyMethodProcessor 进行 Response 的处理
更新日志
V20210821 更新流程图及总结
本文转载自: 掘金