转载

【译】理解Spring MVC Model Attribute 和 Session Attribute

作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域。在设计和构建 Java Web 应用时,理解这些作用域,如何将数据与对象和这些作用域交互是十分重要的。【在 StackOverflow 上有一篇 文章 可以帮助你快速了解 request 和 session 作用域】

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。

 1 public class MyController {  2    3     @ModelAttribute("myRequestObject")  4     public MyCommandBean addStuffToRequestScope() {  5         System.out.println("Inside of addStuffToRequestScope");  6         MyCommandBean bean = new MyCommandBean("Hello World",42);  7         return bean;  8     }  9   10     @RequestMapping("/dosomething") 11     public String requestHandlingMethod(Model model, HttpServletRequest request) { 12         System.out.println("Inside of dosomething handler method"); 13   14         System.out.println("--- Model data ---"); 15         Map modelMap = model.asMap(); 16         for (Object modelKey : modelMap.keySet()) { 17             Object modelValue = modelMap.get(modelKey); 18             System.out.println(modelKey + " -- " + modelValue); 19         } 20   21         System.out.println("=== Request data ==="); 22         java.util.Enumeration reqEnum = request.getAttributeNames(); 23         while (reqEnum.hasMoreElements()) { 24             String s = reqEnum.nextElement(); 25             System.out.println(s); 26             System.out.println("==" + request.getAttribute(s)); 27         } 28   29         return "nextpage"; 30     } 31   32          //  ... the rest of the controller 33 }

在一个到达的 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。

【译】理解Spring MVC Model Attribute 和 Session Attribute

当这个小小的网址被修改为这种形式后,controller 的 System.out.println 展现了 @ModelAttribute 方法是如何在 handler method 之前执行的。同时也展现了 MyCommandBean 创建和加入 Spring model,并在 handler method 中可用的过程。

 1 Inside of addStuffToRequestScope  2 Inside of dosomething handler method  3 --- Model data ---  4 myRequestObject -- MyCommandBean [someString=Hello World, someNumber=42]  5 === Request data ===  6 org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE  7 ==WebApplicationContext for namespace 'dispatcher-servlet': startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy  8 org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER  9 ==org.springframework.web.servlet.theme.FixedThemeResolver@204af48c 10 org.springframework.web.servlet.DispatcherServlet.CONTEXT 11 ==WebApplicationContext for namespace 'dispatcher-servlet': startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy 12 org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping 13 ==dosomething.request 14 org.springframework.web.servlet.HandlerMapping.bestMatchingPattern 15 ==/dosomething.* 16 org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER 17 ==org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@18fd23e4

现在问题变为了『Spring model 的数据存储在哪里?』是存储在标准 Java request 作用域中么?答案是 —— 对的。。。就最终而言的话。就像你从上面的输出中读到的,MyCommandBean 在 model 中,但当 handler method 执行时还不在 request 对象中。的确,handler method 执行之后,下个视图(本例中为 nextpage.jsp)显示之前为止,Spring 都没有将 model 数据作为 attribute 加入 request 中。

【译】理解Spring MVC Model Attribute 和 Session Attribute

也可以通过输出存储在 index.jsp 与 nextpage.jsp 的 HttpServletRequest 中的 attribute 展现出来。我在两个页面中都布置了一个 JSP 代码块(如下面所示),用以展现 HttpServletRequest 的 attribute。

 1 <HR />  2 <H3>REQUEST SCOPE (KEY==VALUES)</H3>  3 <%  4     JAVA.UTIL.ENUMERATION<STRING> REQENUM = REQUEST.GETATTRIBUTENAMES();  5     WHILE (REQENUM.HASMOREELEMENTS()) {  6         STRING S = REQENUM.NEXTELEMENT();  7         OUT.PRINT(S);  8         OUT.PRINTLN("==" + REQUEST.GETATTRIBUTE(S));  9 %><BR /> 10 <% 11     } 12 %>

当应用启动,index.jsp 加载完毕,你可以看到在 request 作用域中没有 attribute。

【译】理解Spring MVC Model Attribute 和 Session 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 MVC Model Attribute 和 Session Attribute

所以下一个视图呈现之前,Spring model 数据已经在 handler method 执行之前(或者之间)被拷贝给了 HttpServletRequest。

使用 SPRING MODEL 与 REQUEST 的原因

你或许想知道为什么 Spring 使用 model attribute。为何不直接把数据加到 request 对象里?我在 Rod Johnson 等人的书籍 《Professional Java Development with the Spring Framework》 中找到了答案。这本书关于 Spring API 的部分有一点过时(基于 Spring 2.0 编写),但是我发现该书提供了一些对于 Spring 引擎运行的扩展解释。下面是书中 model 元素部分的引用:

直接将元素加入 HttpServletRequest(像 request attributes 一样)看起来就像在服务同样的目标。这样做的理由是当看到我们为 MVC 框架设置的 requirements 时,能够更明确。它应尽可能与视图无关,这意味着我们可以合并视图技术,并不受 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。

 1 <H3>SESSION SCOPE (KEY==VALUES)</H3>  2 <%  3   JAVA.UTIL.ENUMERATION<STRING> SESSENUM = REQUEST.GETSESSION()  4     .GETATTRIBUTENAMES();  5   WHILE (SESSENUM.HASMOREELEMENTS()) {  6     STRING S = SESSENUM.NEXTELEMENT();  7     OUT.PRINT(S);  8     OUT.PRINTLN("==" + REQUEST.GETSESSION().GETATTRIBUTE(S));  9 %><BR /> 10 <% 11   } 12 %>

我使用 @SessionAttributes 注解 MyController,使其将同一个 model attributes(myRequestObject)放入 Spring session 中。

1 @CONTROLLER 2 @SESSIONATTRIBUTES("MYREQUESTOBJECT") 3 PUBLIC CLASS MYCONTROLLER { 4   ... 5 }

另外在 controller 的 handler method 中添加代码显示 HttpSession 中的 attributes(就像显示 HttpServletRequest 中的 attributes 一样)。

 1 @SUPPRESSWARNINGS("RAWTYPES")  2 @REQUESTMAPPING("/DOSOMETHING")  3 PUBLIC STRING REQUESTHANDLINGMETHOD(MODEL MODEL, HTTPSERVLETREQUEST REQUEST, HTTPSESSION SESSION) {  4   SYSTEM.OUT.PRINTLN("INSIDE OF DOSOMETHING HANDLER METHOD");  5   6   SYSTEM.OUT.PRINTLN("--- MODEL DATA ---");  7   MAP MODELMAP = MODEL.ASMAP();  8   FOR (OBJECT MODELKEY : MODELMAP.KEYSET()) {  9     OBJECT MODELVALUE = MODELMAP.GET(MODELKEY); 10     SYSTEM.OUT.PRINTLN(MODELKEY + " -- " + MODELVALUE); 11   } 12  13   SYSTEM.OUT.PRINTLN("=== REQUEST DATA ==="); 14   JAVA.UTIL.ENUMERATION<STRING> REQENUM = REQUEST.GETATTRIBUTENAMES(); 15   WHILE (REQENUM.HASMOREELEMENTS()) { 16     STRING S = REQENUM.NEXTELEMENT(); 17     SYSTEM.OUT.PRINTLN(S); 18     SYSTEM.OUT.PRINTLN("==" + REQUEST.GETATTRIBUTE(S)); 19   } 20  21   SYSTEM.OUT.PRINTLN("*** SESSION DATA ***"); 22   ENUMERATION<STRING> E = SESSION.GETATTRIBUTENAMES(); 23   WHILE (E.HASMOREELEMENTS()){ 24     STRING S = E.NEXTELEMENT(); 25     SYSTEM.OUT.PRINTLN(S); 26     SYSTEM.OUT.PRINTLN("**" + SESSION.GETATTRIBUTE(S)); 27   } 28  29   RETURN "NEXTPAGE"; 30 }

现在,我们可以看见加上 @SessionAttributes 注解后,Spring MVC 处理一个 HTTP 请求之前、之间和之后的 session 对象情况。下面显示了结果。首先,当 index.jsp 显示时(请求被 Spring MVC 发送和处理之前),我们可以看见 HttpServletRequest 和 HttpSession 都没有 attribute 数据。

【译】理解Spring MVC Model Attribute 和 Session Attribute

handler method 执行时(requestHandlingMethod),你可以看见 MyCommandBean 被添加进 Spring model attributes,但是还没有加入 HttpServletRequest 或 HttpSession 作用域。

【译】理解Spring MVC Model Attribute 和 Session Attribute

但是 handler method 执行后和 nextpage.jsp 显示时,你可以看见 model attribute 数据(MyCommandBean)已经作为一个 attribute 被复制给了 HttpServletRequest 和 HttpSession(拥有相同的 attribute key)。

【译】理解Spring MVC Model Attribute 和 Session Attribute

控制 SESSION ATTRIBUTES

现在你已经理解了 Spring model 和 session attribute 数据如何添加进 HttpServletRequest 与 HttpSession。或许又开始关心怎么管理 Spring session 中的数据。Spring 提供了一个方法移除 Spring session attributes,同时也会从 HttpSession 中移除(不需要删除整个 HttpSession)。简单地将一个 Spring SessionStatus 对象作为参数加入一个 controller handler method 中。在此方法中,使用 SessionStatus 对象结束这个 Spring session。

1 @REQUESTMAPPING("/ENDSESSION") 2 PUBLIC STRING NEXTHANDLINGMETHOD2(SESSIONSTATUS STATUS){ 3   STATUS.SETCOMPLETE(); 4   RETURN "LASTPAGE"; 5 }

总结

希望这篇文章能够帮助你理解 Spring model 和 session attributes。这并不神奇,仅仅是一个理解 HttpSession 和 HttpServletRequest 如何存储 Spring model 和 session attributes 的问题。我已经将展示用的代码放在了 Intertech Web site 上。如果你对继续探索与理解 Spring model 和 session 感兴趣,尽管从 这里 下载吧。

如果你对深入学习 Spring(或任何一种 Java 技术)感兴趣,可以考虑马上注册,成为 Intertech 的一员。在 这里 学到更多与注册。

原文链接: Intertech

首发于 importnew,译文链接:  http://www.importnew.com/16782.html

正文到此结束
Loading...