在平时开发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.最后是一个转发操作