SpringMVC_第四部分 Spring MVC 源码深度剖析
第 1 节 前端控制器 DispatcherServlet 继承结构
第 2 节 重要时机点分析
1
)
Handler
⽅法的执⾏时机
打断点
观察调⽤栈
![](https://i-blog.csdnimg.cn/blog_migrate/473856bdc08aa7ac97540cd7503f872a.png)
doDispathch
⽅法中的
1064
⾏代码完成
handler
⽅法的调⽤
2
)⻚⾯渲染时机(打断点并观察调⽤栈)
![](https://i-blog.csdnimg.cn/blog_migrate/3e525689f5415aade8f14a9e1740336d.png)
SpringMVC
处理请求的流程即为
org.springframework.web.servlet.DispatcherServlet#doDispatch
⽅法的执⾏过程,其中步骤
2
、
3
、
4
、
5
是核⼼步骤
1
)调⽤
getHandler()
获取到能够处理当前请求的执⾏链
HandlerExecutionChain
(
Handler+
拦截
器)
但是如何去
getHandler
的?后⾯进⾏分析
2
)调⽤
getHandlerAdapter()
;获取能够执⾏
1
)中
Handler
的适配器
但是如何去
getHandlerAdapter
的?后⾯进⾏分析
3
)适配器调⽤
Handler
执⾏
ha.handle
(总会返回⼀个
ModelAndView
对象)
4
)调⽤
processDispatchResult()
⽅法完成视图渲染跳转
protected void doDispatch ( HttpServletRequest request , HttpServletResponseresponse ) throws Exception {HttpServletRequest processedRequest = request ;HandlerExecutionChain mappedHandler = null ;boolean multipartRequestParsed = false ;WebAsyncManager asyncManager = WebAsyncUtils . getAsyncManager ( request );try {ModelAndView mv = null ;Exception dispatchException = null ;try {// 1 检查是否是⽂件上传的请求processedRequest = checkMultipart ( request );multipartRequestParsed = ( processedRequest != request );// Determine handler for the current request./*2 取得处理当前请求的 Controller ,这⾥也称为 Handler ,即处理器这⾥并不是直接返回 Controller ,⽽是返回 HandlerExecutionChain 请求处理链对象该对象封装了 Handler 和 Inteceptor*/mappedHandler = getHandler ( processedRequest );if ( mappedHandler == null ) {// 如果 handler 为空,则返回 404noHandlerFound ( processedRequest , response );return ;}// Determine handler adapter for the current request.// 3 获取处理请求的处理器适配器 HandlerAdapterHandlerAdapter ha = getHandlerAdapter ( mappedHandler . getHandler ());// Process last-modified header, if supported by the handler.// 处理 last-modified 请求头String method = request . getMethod ();boolean isGet = "GET" . equals ( method );if ( isGet || "HEAD" . equals ( method )) {long lastModified = ha . getLastModified ( request ,mappedHandler . getHandler ());if ( new ServletWebRequest ( request ,response ). checkNotModified ( lastModified ) && isGet ) {return ;}}if ( ! mappedHandler . applyPreHandle ( processedRequest , response )) {return ;}// Actually invoke the handler.// 4 实际处理器处理请求,返回结果视图对象mv = ha . handle ( processedRequest , response ,mappedHandler . getHandler ());if ( asyncManager . isConcurrentHandlingStarted ()) {return ;}// 结果视图对象的处理applyDefaultViewName ( processedRequest , mv );mappedHandler . applyPostHandle ( processedRequest , response , mv );}catch ( Exception ex ) {dispatchException = ex ;}catch ( Throwable err ) {// As of 4.3, we're processing Errors thrown from handler methodsas well,// making them available for @ExceptionHandler methods and otherscenarios.dispatchException = new NestedServletException ( "Handler dispatchfailed" , err );}// 5 跳转⻚⾯,渲染视图processDispatchResult ( processedRequest , response , mappedHandler , mv ,dispatchException );}catch ( Exception ex ) {// 最终会调⽤ HandlerInterceptor 的 afterCompletion ⽅法triggerAfterCompletion ( processedRequest , response , mappedHandler ,ex );}catch ( Throwable err ) {// 最终会调⽤ HandlerInterceptor 的 afterCompletion ⽅法triggerAfterCompletion ( processedRequest , response , mappedHandler ,new NestedServletException ( "Handler processing failed" , err ));}finally {if ( asyncManager . isConcurrentHandlingStarted ()) {// Instead of postHandle and afterCompletionif ( mappedHandler != null ) {mappedHandler . applyAfterConcurrentHandlingStarted ( processedRequest ,response );}}else {// Clean up any resources used by a multipart request.if ( multipartRequestParsed ) {cleanupMultipart ( processedRequest );}}}}
第 3 节 核⼼步骤getHandler⽅法剖析
遍历两个
HandlerMapping
,试图获取能够处理当前请求的执⾏链
![](https://i-blog.csdnimg.cn/blog_migrate/c624650eb5923f4efd20e185c3a5ec3c.png)
第 4 节 核⼼步骤getHandlerAdapter⽅法剖析
遍历各个
HandlerAdapter
,看哪个
Adapter
⽀持处理当前
Handler
![](https://i-blog.csdnimg.cn/blog_migrate/6e23789b78563eb5a89f754480298ee9.png)
第 5 节 核⼼步骤ha.handle⽅法剖析
⼊⼝
![](https://i-blog.csdnimg.cn/blog_migrate/d103ab8563012221adcf2ec1ac010a86.png)
断点从⼊⼝进⼊
![](https://i-blog.csdnimg.cn/blog_migrate/54b96bc7b500886d3c7b2eab8b7c9876.png)
第 6 节 核⼼步骤processDispatchResult⽅法剖析render⽅法完成渲染
render
⽅法完成渲染
![](https://i-blog.csdnimg.cn/blog_migrate/cece2e73baa8c23a3f4f0f5b0fa79b07.png)
视图解析器解析出
View
视图对象
![](https://i-blog.csdnimg.cn/blog_migrate/2af3f1715732a47541da1a71e7d52d39.png)
在解析出
View
视图对象的过程中会判断是否重定向、是否转发等,不同的情况封装的是不同的
View
实现
![](https://i-blog.csdnimg.cn/blog_migrate/cdd0146e7eb059c9ab3b7dbf3301775e.png)
解析出View视图对象的过程中,要将逻辑视图名解析为物理视图名
封装View视图对象之后,调⽤了view对象的render⽅法
渲染数据
把
modelMap
中的数据暴露到
request
域中,这也是为什么后台
model.add
之后在
jsp
中可以从请求
域取出来的根本原因
将数据设置到请求域中
第 7 节 SpringMVC九⼤组件初始化
1)在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件
/** MultipartResolver used by this servlet. */// 多部件解析器@Nullableprivate MultipartResolver multipartResolver ;/** LocaleResolver used by this servlet. */// 区域化 国际化解析器@Nullableprivate LocaleResolver localeResolver ;/** ThemeResolver used by this servlet. */// 主题解析器@Nullableprivate ThemeResolver themeResolver ;/** List of HandlerMappings used by this servlet. */// 处理器映射器组件@Nullableprivate List < HandlerMapping > handlerMappings ;/** List of HandlerAdapters used by this servlet. */// 处理器适配器组件@Nullableprivate List < HandlerAdapter > handlerAdapters ;/** List of HandlerExceptionResolvers used by this servlet. */// 异常解析器组件@Nullableprivate List < HandlerExceptionResolver > handlerExceptionResolvers ;/** RequestToViewNameTranslator used by this servlet. */// 默认视图名转换器组件@Nullableprivate RequestToViewNameTranslator viewNameTranslator ;/** FlashMapManager used by this servlet. */// flash 属性管理组件@Nullableprivate FlashMapManager flashMapManager ;/** List of ViewResolvers used by this servlet. */// 视图解析器@Nullableprivate List < ViewResolver > viewResolvers ;
九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如
ViewResolver
、
HandlerAdapter 等都是接⼝
2)九⼤组件的初始化时机
DispatcherServlet
中的
onRefresh()
,该⽅法中初始化了九⼤组件
![](https://i-blog.csdnimg.cn/blog_migrate/9334ddf5a001253ca8216dfcdae96d46.png)
initStrategies
⽅法
![](https://i-blog.csdnimg.cn/blog_migrate/c99c0bd36408e43eaffbfe80a1f795e7.png)
观察其中的⼀个组件
initHandlerMappings(context)
![](https://i-blog.csdnimg.cn/blog_migrate/f704312d88366917cdd44030dd5d846d.png)
如果按照类型和按照固定
id
从
ioc
容器中找不到对应组件,则会按照默认策略进⾏注册初始化,默
认策略在
DispatcherServlet.properties
⽂件中配置
![](https://i-blog.csdnimg.cn/blog_migrate/4ef98ee0ddf56a64dc9912eedf542a54.png)
DispatcherServlet.properties
![](https://i-blog.csdnimg.cn/blog_migrate/a1a3563c5d24940db6ab427d504fe748.png)
注意:多部件解析器的初始化必须按照
id
注册对象(
multipartResolver
)
![](https://i-blog.csdnimg.cn/blog_migrate/1124fd70de6dffae16e8a4c98fa96dd5.png)