SpringtMVC运行流程:@RequestMapping 方法中的 Map、HttpServletRequest等参数信息是如何封装和传递的(源码理解)

在平时开发SpringtMVC程序时,在Controller的方法上,通常会传入如Map、HttpServletRequest类型的参数,并且可以方便地向里面添加数据。同时,在Jsp中还可以直接使用request等对象方便地获取出来。

如下面2图所示:

 

可问题是:@RequestMapping 方法中的 Map、HttpServletRequest等参数信息是如何封装和传递的?

带着这个问题,写了个简单的Demo,来进行源码调试。

Demo代码地址:

https://github.com/cyhbyw/springMVC_atguigu_TongGang

工程名称:

springMVC_DebugSourceCode

 

===========以下是源码调试==================

==============>>>>
PS:图片可能不是很清晰,可以右击图片、选择在新标签页中查看
或者,可以右击图片,选择“图片另存为”保存在本地并编好号(建议直接以01、02、03……来编号)
或者,可以右击图片,选择“复制图片”,再保存到本地并编好号(建议直接以01、02、03……来编号)
以上三种办法,任意选择喜欢的一种,以获得并查看更清晰的图片~~
<<<<==============

01.首先,浏览器发出的请求到DispatcherServlet;然后,找到合适的HandlerAdapter(此处是RequestMappingHandlerAdapter);然后调用RequestMappingHandlerAdapter的handle()方法。此时,方法堆栈从Line959开始。

 

  

02.还是在DispatcherServlet的Line959的doDispatch()方法内,又调用了几个方法,到达invokeForRequest()方法;顾名思义,此方法会真正的调用Request方法(即Controller中的方法);不过,先会在Line128解析参数。

 

 

 03.解析参数的方法又会走到Line161.

 

 

 04.再经过两个方法的调用,可以看到上述的参数解析方法是直接返回了 mavContainer.getModle()

而由06步可以看到,Model实际上是个map对象,那么,此处直接返回map对象就相当于返回了其引用,外部任何地方的修改都将影响此map中的值!!!

这个引用会给第08步中的 Object[] args 以及第09、10步中的 map,所以第10步中向 map 中添加数据,其实也就等价于向此 Model 中添加了数据!!!(背景知识:Java对象引用)

 

  

05.而getModel() 方法返回 defaulutModel 的成员变量。

 

  

06. defaulutModel 其实就是一个 BindingAwareModelMap

 

 

 07.回到刚才的Line161,可以看到 args[i] 指向了刚才得到的BindingAwareModelMap@4821。

 

  

08.再返回一层,此处的 Object[] args 还是BindingAwareModelMap@4821;并且到目前为止,其中的元素仍然为空;此处Line136的 doInvoke(args) 方法就是通过反射调用真实的Controller中的方法。

 

  

09.调用真实方法(由08步中的Line136反射调用);可以看到,真实Controller方法中的入参 map 还是BindingAwareModelMap@4821(说明是同一个对象——非常重要!!)

 

 

10.将值写入map中

 

 

11.真实Controller方法调用返回后,可以看到 mavContainer 对象中的 defaultModel 属性已经被赋值,且这个值就是BindingAwareModelMap@4821,与args是同一个对象(非常重要,而且从前面的分析中也可以看到)!!!!

(备注:SpringMVC是如何为mavContainer 对象中的 defaultModel 属性赋值的,最开始调试了很久也没有发现,心想着,它既然是个Map,那应该是调用setXXX(), put(), putAll()这样的方法赋值进去的,但调试了很久始终没发现这样的方法被调用;同时,也可以确定它是在Line136行调用后就被赋值的;最后猜测,是同一个对象引用;现在,证明确实如此)

再啰嗦一句,其实就是:Line128的 Object[] args 变量指向了 mavContainer 对象的 defaultModel 属性!所以在将 args 通过Line136反射调用真实的Controller方法并填充数据后,defaultModel中也就有了相应数据!

注意结合第04步中的文字进行理解!

 

 12.真实Controller方法调用完成后,开始处理返回值

 

 13.设置视图名称

 

 

 14.准备创建ModelAndView对象

 

  

15.从 mavContainer 中取出Model数据,并通过构造函数传入ModelAndView

 

  

16.已经获得ModelAndView对象,进行后续操作(如渲染);注意,此时DispatcherServlet中的方法堆栈从Line971开始。

 

  

17.准备渲染

 

  

18.得到View对象

 

  

19.遍历 viewResolvers 找到一个合适的就返回给18步中的View对象

 

  

20.准备渲染

 

  

21.将Model数据暴露为RequestAttribute

 

  

22.暴露的本质其实是:request.setAttribute(modelName, modelValue)

重要:在这里是 request.setAttribute 所以在 Jsp 中才可以 request.getAttribute,先有 set 才有 get 嘛。

 

  

23.最后是一个转发操作

 

 

posted @ 2018-03-21 21:03  cyhbyw  阅读(9148)  评论(1编辑  收藏  举报