1. 程式人生 > >理解Spring MVC Model Attribute 和 Session Attribute

理解Spring MVC Model Attribute 和 Session Attribute

講解 可用 rar hand 如何 arch next sco elk

作為一名 Java Web 應用開發者,你已經快速學習了 request(HttpServletRequest)和 session(HttpSession)作用域。在設計和構建 Java Web 應用時,理解這些作用域,如何將數據與對象和這些作用域交互是十分重要的。
SPRING MVC 作用域
當我開始用 Spring MVC 編寫 Web 應用時,我發現 Spring model 和 session attribute 有一點神秘,尤其當它們與我熟知的 HTTP request 和 session 作用域交互時。一個 Spring model 元素可以從我的 session 或者 request 中找到嗎?如果是這樣的話,我該如何控制?在這篇文章中,我希望講解清楚 Spring MVC 的 model 與 session 是如何工作的。
SPRING 的 @MODELATTRIBUTE
有幾種方法將數據或對象添加到 Spring 的 model 中。一般來說,數據或對象是通過 controller 層的一個註解添加進 Spring 的 model 中。在下面的例子中,使用 @ModelAttribute 添加一個名為 MyCommandBean 的實例給 key 值為『myRequestObject』的 model。

@Controller

public class MyController {

@ModelAttribute("myRequestObject")
public MyCommandBean addStuffToRequestScope() {
System.out.println("Inside of addStuffToRequestScope");
MyCommandBean bean = new MyCommandBean("Hello World",42);
return bean;
}

@RequestMapping("/dosomething")
public String requestHandlingMethod(Model model, HttpServletRequest request) {
System.out.println("Inside of dosomething handler method");

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("=== Request data ===");
java.util.Enumeration reqEnum = request.getAttributeNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println(s);
System.out.println("==" + request.getAttribute(s));
}

return "nextpage";
}

//  ... the rest of the controller
}

在一個到達的 request 中,任何被 @ModelAttribute 註解的方法都會在 controller handler method 之前調用(就像上面例子中的 requestHandlingMethod 一樣)。這些方法會趕在 handler method 執行之前將數據添加進一個 java.util.Map,然後加入 Spring model 中。可以用一個示例操作展示出來。我創建了兩個 JSP 頁面:index.jsp 和 nextpage.jsp。index.jsp 上的一個鏈接用於向 MyController 中的 requestHandlingMethod() 應用觸發器發送一個 request。上面的代碼中,requestHandlingMethod() 將『nextpage』作為下個視圖的邏輯名返回,其在這個例子中會處理為 nextpage.jsp。

技術分享圖片
當這個小小的網址被修改為這種形式後,controller 的 System.out.println 展現了 @ModelAttribute 方法是如何在 handler method 之前執行的。同時也展現了 MyCommandBean 創建和加入 Spring model,並在 handler method 中可用的過程。

Inside of addStuffToRequestScope
Inside of dosomething handler method
--- Model data ---
myRequestObject -- MyCommandBean [someString=Hello World, someNumber=42]
=== Request data ===
org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE
==WebApplicationContext for namespace ‘dispatcher-servlet‘: startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy
org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER
==org.springframework.web.servlet.theme.FixedThemeResolver@204af48c
org.springframework.web.servlet.DispatcherServlet.CONTEXT
==WebApplicationContext for namespace ‘dispatcher-servlet‘: startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy
org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
==dosomething.request
org.springframework.web.servlet.HandlerMapping.bestMatchingPattern
==/dosomething.*
org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER
==org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@18fd23e4

現在問題變為了『Spring model 的數據存儲在哪裏?』是存儲在標準 Java request 作用域中麽?答案是 —— 對的。。。就最終而言的話。就像你從上面的輸出中讀到的,MyCommandBean 在 model 中,但當 handler method 執行時還不在 request 對象中。的確,handler method 執行之後,下個視圖(本例中為 nextpage.jsp)顯示之前為止,Spring 都沒有將 model 數據作為 attribute 加入 request 中。
技術分享圖片

也可以通過輸出存儲在 index.jsp 與 nextpage.jsp 的 HttpServletRequest 中的 attribute 展現出來。我在兩個頁面中都布置了一個 JSP 代碼塊(如下面所示),用以展現 HttpServletRequest 的 attribute。

<hr />
<h3>Request Scope (key==values)</h3>
<%
java.util.Enumeration<String> reqEnum = request.getAttributeNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
out.print(s);
out.println("==" + request.getAttribute(s));
%>
<%
}
%>

當應用啟動,index.jsp 加載完畢,你可以看到在 request 作用域中沒有 attribute。

技術分享圖片
在本例中,當『do something』被點擊時執行 MyController 的 handler method,然後會跳轉並展示 nextpage.jsp。而 nextpage.jsp 中已經編寫了相同的 JSP 代碼塊,同樣提供了 request 作用域中的 attribute。瞧,當 nextpage.jsp 渲染後,顯示出在 controller 中創建的 MyCommandBean model 被加進 HttpServletRequest 作用域中了!Spring model attribute 的鍵值『myRequestObject』被復制後用作 request attribute 的鍵值。
技術分享圖片

所以下一個視圖呈現之前,Spring model 數據已經在 handler method 執行之前(或者之間)被拷貝給了 HttpServletRequest。

SPRING 的 @SESSIONATTRIBUTES
所以現在你知道了 Spring 如何管理 model 數據,與如何連接標準的 Http request attribute 數據。那麽關於 Spring 的 session 數據呢?

Spring 的 @SessionAttribute 在 controller 中用來指定哪一個 model attributes 需要存儲到 session。事實上,Spring 文檔聲明了 @SessionAttributes 註解『列舉需要顯式地存儲 session 或一些交互用的存儲空間內的 model attributes 名稱。』另外說一下,『一些交互存儲空間』表明了 Spring MVC 試圖保持與技術無關聯的設計思想。

事實上,@SessionAttributes 允許你做的就是告訴 Spring 哪一個 model attributes 將在視圖展現之前一同拷貝給 HttpSession。關於這一點同樣可以用一個簡短的代碼來展示。


在 index.jsp 和 nextpage.jsp 中,我添加了額外的 JSP 代碼塊,使其顯示 HttpSession attributes。

<h3>Session Scope (key==values)</h3>
<%
  java.util.Enumeration<String> sessEnum = request.getSession()
.getAttributeNames();
  while (sessEnum.hasMoreElements()) {
String s = sessEnum.nextElement();
out.print(s);
out.println("==" + request.getSession().getAttribute(s));
%>
<%
  }
%>

我使用 @SessionAttributes 註解 MyController,使其將同一個 model

attributes(myRequestObject)放入 Spring session 中。
@Controller
@SessionAttributes("myRequestObject")
public class MyController {
  ...
}

另外在 controller 的 handler method 中添加代碼顯示 HttpSession 中的 attributes(就像顯示 HttpServletRequest 中的 attributes 一樣)。

@SuppressWarnings("rawtypes")
@RequestMapping("/dosomething")
public String requestHandlingMethod(Model model, HttpServletRequest request, HttpSession session) {
  System.out.println("Inside of dosomething handler method");

  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("=== Request data ===");
  java.util.Enumeration<String> reqEnum = request.getAttributeNames();
  while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println(s);
System.out.println("==" + request.getAttribute(s));
  }

  System.out.println("*** Session data ***");
  Enumeration<String> e = session.getAttributeNames();
  while (e.hasMoreElements()){
String s = e.nextElement();
System.out.println(s);
System.out.println("**" + session.getAttribute(s));
  }

  return "nextpage";
}

現在,我們可以看見加上 @SessionAttributes 註解後,Spring MVC 處理一個 HTTP 請求之前、之間和之後的 session 對象情況。下面顯示了結果。首先,當 index.jsp 顯示時(請求被 Spring MVC 發送和處理之前),我們可以看見 HttpServletRequest 和 HttpSession 都沒有 attribute 數據。
技術分享圖片

handler method 執行時(requestHandlingMethod),你可以看見 MyCommandBean 被添加進 Spring model attributes,但是還沒有加入 HttpServletRequest 或 HttpSession 作用域。
技術分享圖片
但是 handler method 執行後和 nextpage.jsp 顯示時,你可以看見 model attribute 數據(MyCommandBean)已經作為一個 attribute 被復制給了 HttpServletRequest 和 HttpSession(擁有相同的 attribute key)。
技術分享圖片
控制 SESSION ATTRIBUTES
現在你已經理解了 Spring model 和 session attribute 數據如何添加進 HttpServletRequest 與 HttpSession。或許又開始關心怎麽管理 Spring session 中的數據。Spring 提供了一個方法移除 Spring session attributes,同時也會從 HttpSession 中移除(不需要刪除整個 HttpSession)。簡單地將一個 Spring SessionStatus 對象作為參數加入一個 controller handler method 中。在此方法中,使用 SessionStatus 對象結束這個 Spring session。

@RequestMapping("/endsession")
public String nextHandlingMethod2(SessionStatus status){
  status.setComplete();
  return "lastpage";
}

總結
希望這篇文章能夠幫助你理解 Spring model 和 session attributes。這並不神奇,僅僅是一個理解 HttpSession 和 HttpServletRequest 如何存儲 Spring model 和 session attributes 的問題。我已經將展示用的代碼放在了 Intertech Web site 上。
感謝大家的閱讀,喜歡小編的文章可以關註一下,小編會不定時更新哦

理解Spring MVC Model Attribute 和 Session Attribute