Spring MVC是個很是優秀的框架,其優秀之處繼承自Spring自己依賴注入(Dependency Injection)的強大的模塊化和可配置性,其設計到處透露着易用性、可複用性與易集成性。優良的設計模式遍佈各處,使得其框架雖然學習曲線陡峭,但 一旦掌握則欲罷不能。初學者並不須要過多瞭解框架的實現原理,隨便搜一下如何使用「基於註解的controller」就能很快上手,而一些書籍諸如 「spring in action」也給上手提供了很是優良的選擇。java
網上的帖子多如牛毛,中文的快速上手,英文的深刻淺出。這樣看來,Spring的學習簡直是一個輕鬆愉快的過程。web
可是!!spring
關於Spring中session的使用,大部分資料都諱莫如深。也許這個問題太過容易推斷出?大部分資料都沒有包括我下面所將要陳述的內容。關於Spring中session的正確使用方法,這裏甚至建議直接使用HttpSession。但這種方法顯然違背了Spring 「technology agnostic」 (這個名詞我理解意思就是不管你是在什麼具體的應用中使用相似的控制邏輯,servlet、一個本地JVM程序或者其餘,你的Controller均可以獲得複用)的初衷。設計模式
因而我開始從龐大的網絡資源和書籍中搜索關於Session的正確用法及Spring MVC處理Session的機制,其中講得最深刻並且清楚的算是這一篇。從上文的內容,及我所查閱的好比官方文檔這種資料中,我能夠大約推斷出幾個要點:spring-mvc
1. Spring框架會在調用完Controller以後、渲染View以前檢查Model的信息,並把@SessionAttributes()註釋標明的屬性加入session中網絡
2. @ModelAttribute在聲明Controller的參數的時候,能夠用來代表此參數引用某個存在在Model中的對 象,若是這個對象已經存在於Model中的話(Model能夠在調用Controller以前就已經保存有數據,這應該不只僅由於 HandlerInterceptor或者@ModelAttribute標記的方法已經顯式的將一些對象加入到了Model對象中,也由於Spring會默認將一些對象加入到Model中,這一點很重要)。session
3. 若是Session中已經存在某個對象,那麼能夠直接使用ModelAttribute聲明Controller的參數,在Controller中能夠直接使用它。mvc
其中1很明確,我提到的那篇文章主要就在說明這一點。而從2和3咱們也許能夠大膽地推出一個結論:app
Spring會在調用Controller以前將session中的對象填入Model中框架
由於想從2獲得3,這個結論就顯得比較天然。那麼事實上是否是如此呢?能夠作一個小實驗。仿效我所引用的那篇文章,我寫了以下代碼:
@Controller
@RequestMapping("/user") @SessionAttributes("userId") public class UserController { @RequestMapping(value="/login", method=GET) public String login ( int id, Model model, HttpServletRequest request, HttpSession session) { model.addAttribute("userId", id); System.out.println(""); System.out.println(""); System.out.println("inside login"); System.out.println(""); System.out.println("--- Model data ---"); Map modelMap = model.asMap(); for (Object modelKey : modelMap.keySet()) { Object modelValue = modelMap.get(modelKey); System.out.println(modelKey + " -- " + modelValue); } System.out.println(""); System.out.println("*** Session data ***"); Enumeration<String> e = session.getAttributeNames(); while (e.hasMoreElements()) { String s = e.nextElement(); System.out.println(s + " == " + session.getAttribute(s)); } return "/test"; } @RequestMapping(value="/check", method=GET) public String check ( Model model, HttpServletRequest request, HttpSession session) { System.out.println(""); System.out.println(""); System.out.println("inside check"); System.out.println(""); System.out.println("--- Model data ---"); Map modelMap = model.asMap(); for (Object modelKey : modelMap.keySet()) { Object modelValue = modelMap.get(modelKey); System.out.println(modelKey + " -- " + modelValue); } System.out.println(""); System.out.println("*** Session data ***"); Enumeration<String> e = session.getAttributeNames(); while (e.hasMoreElements()) { String s = e.nextElement(); System.out.println(s + " == " + session.getAttribute(s)); } return "/test"; } }
而test.jsp的做用就是把Session中的對象打印出來。
調用的順序是,在首先保證Session爲空的狀況下,前後輸入如下連接:
http://localhost:8080/XX/user/check
http://localhost:8080/XX/user/login?id=1
http://localhost:8080/XX/user/check
頁面的顯示結果分別爲:
1
2
3
而Tomcat的輸出結果爲:
inside check
--- Model data ---
*** Session data ***
inside login
--- Model data ---
userId -- 1
*** Session data ***
inside check
--- Model data ---
userId -- 1
*** Session data ***
userId == 1
結果如我所料。首先Session中並無userId屬性,在某個Controller加入了它以後,隨後的Controller中的Model 會自動加入已經存在於Session的對象。雖然確實有不少不少帖子提到了@SessionAttributes並非使用session的正確方法,但 是如實驗所得,使用它使得最終屬性都加入到了HttpSession對象中,夫復何求?(這裏也許須要更多的討論,我倒但願能有什麼更值得信服的說法讓我 乖乖用回HttpSession)。那麼,在Spring中使用Session的一個相對比較「technology agnostic」的方法就是:
1 使用@SessionAttributes提示框架哪些屬性須要存在Session中
2 在某些Controller中將這些屬性加入到Model中
3 在另一些Controler中直接使用這些屬性
4 在其餘Controller中判斷Model中是否存在相應屬性,以肯定Session中是否已經註冊了這個屬性