【译】理解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。

当这个小小的网址被修改为这种形式后,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_第1张图片

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

 1 <h3>Request Scope (key==values)h3>
 2 <%
 3     java.util.Enumeration<String> reqEnum = request.getAttributeNames();
 4     while (reqEnum.hasMoreElements()) {
 5         String s = reqEnum.nextElement();
 6         out.print(s);
 7         out.println("==" + request.getAttribute(s));
 8 %><br />
 9 <%
10     }
11 %>

 

当应用启动,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 MVC Model Attribute 和 Session Attribute_第2张图片

所以下一个视图呈现之前,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 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 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 数据。

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

【译】理解Spring MVC Model Attribute 和 Session Attribute_第3张图片

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

【译】理解Spring MVC Model Attribute 和 Session Attribute_第4张图片

控制 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

已同步至 Github,欢迎 Star 关注更新。

转载于:https://www.cnblogs.com/honoka/p/4886753.html

你可能感兴趣的:(测试,java)