在平時開發SpringtMVC程式時,在Controller的方法上,通常會傳入如Map、HttpServletRequest類型的參數,並且可以方便地向裡面添加數據。同時,在Jsp中還可以直接使用request等對象方便地獲取出來。 如下麵2圖所示: 可問題是:@RequestMapping 方法 ...
在平時開發SpringtMVC程式時,在Controller的方法上,通常會傳入如Map、HttpServletRequest類型的參數,並且可以方便地向裡面添加數據。同時,在Jsp中還可以直接使用request等對象方便地獲取出來。
如下麵2圖所示:
可問題是:@RequestMapping 方法中的 Map、HttpServletRequest等參數信息是如何封裝和傳遞的?
帶著這個問題,寫了個簡單的Demo,來進行源碼調試。
Demo代碼地址:
https://github.com/cyhbyw/springMVC_atguigu_TongGang
工程名稱:
springMVC_DebugSourceCode
===========以下是源碼調試==================
========PS:圖片可能不是很清晰,可以右擊圖片、選擇在新標簽頁中查看(效果更好)=====
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.最後是一個轉發操作