理解Spring MVC Model Attribute 和 Session Attribute

Posted ImportNew

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Spring MVC Model Attribute 和 Session Attribute相关的知识,希望对你有一定的参考价值。


原文:Intertech

译文:ImportNew - honoka

链接:http://www.importnew.com/16782.html


作为一名 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。


@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 中。


理解Spring MVC Model Attribute 和 Session Attribute


也可以通过输出存储在 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。


理解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。


<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 数据。


理解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)。



控制 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 model 和 session 感兴趣,尽管从这里下载吧。


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



ImportNew

--------------------------------------

商务合作QQ:2302462408

投稿网址:top.jobbole.com

以上是关于理解Spring MVC Model Attribute 和 Session Attribute的主要内容,如果未能解决你的问题,请参考以下文章

个人对于spring mvc框架的理解

spring boot入门与理解MVC三层架构

spring mvc面试题

8spring mvc

MVC模式中的Model的理解

如何理解MVC中的Model?