spring modelAttributes、sessionAttributes、redirectAttributes

作为一个javaweb应用的开发者,你快速学习了request(HttpRequest)和Session(HttpSession)的范围,理解这些范围并且在这些范围内数据和对象是如何是进出的对设计和构建web应用是非常关键的。

springMVC的范围

当我用springMVC写web应用的时候,我发现spring model和session有一点神秘—特别是与http reques、和session范围关联起来这些我都已经了解了。spring的model元素会在我的session或者request中找到吗,如果是这样,我怎么控制这个过程呢,在这篇文章中我希望能够解密springMVC的model和session是如何工作的。

spring的@MODELATTRIBUTE

这里有好几种向spring的Model添加数据的方式。数据或者对象通常通过在controller上的注释方法添加到spring中的model中去。下边这个例子中,@ModelAttribute用来将MyCommandBean的实例以key值为myRequestObject添加到model中去
[java]  view plain  copy
  1. @Controller  
  2. public class MyController {  
  3.   
  4.     @ModelAttribute("myRequestObject")  
  5.     public MyCommandBean addStuffToRequestScope() {  
  6.         System.out.println("Inside of addStuffToRequestScope");  
  7.         MyCommandBean bean = new MyCommandBean("Hello World",42);  
  8.         return bean;  
  9.     }  
  10.   
  11.     @RequestMapping("/dosomething")  
  12.     public String requestHandlingMethod(Model model, HttpServletRequest request) {  
  13.         System.out.println("Inside of dosomething handler method");  
  14.   
  15.         System.out.println("--- Model data ---");  
  16.         Map modelMap = model.asMap();  
  17.         for (Object modelKey : modelMap.keySet()) {  
  18.             Object modelValue = modelMap.get(modelKey);  
  19.             System.out.println(modelKey + " -- " + modelValue);  
  20.         }  
  21.   
  22.         System.out.println("=== Request data ===");  
  23.         java.util.Enumeration reqEnum = request.getAttributeNames();  
  24.         while (reqEnum.hasMoreElements()) {  
  25.             String s = reqEnum.nextElement();  
  26.             System.out.println(s);  
  27.             System.out.println("==" + request.getAttribute(s));  
  28.         }  
  29.   
  30.         return "nextpage";  
  31.     }  
  32.   
  33.          //  ... the rest of the controller  
  34. }  

在一个请求的request中,任何使用@ModelAttribute注解的方法会在controller的handler方法(像上边例子汇总的requestHandlingMethod 方法)之前被调用。在这些handler方法执行前,这些方法把数据增加到java.util.map中最终添加到spring Model中去。这可以通过一个简单的实验证明,我创建了两个jsp页面:index.jsp和nextpage.jsp。index.jsp中的链接用来发送request到web应用中来触发Mycontroller中的requestHandlingMethod()方法。requestHandlingMethod()方法返回“nextpage”作为下一个视图逻辑上的名字,在这个例子中我们解析为nextpage.jsp。
当这个小的web站点用这种方式执行的时候,controller里边的System.out.println方法表明了@ModelAttribute方法是如何在handler方法之前运行的。它同样也展示了这个MyCommandBean被创建和添加到springModel中去的过程。
[java]  view plain  copy
  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  
现在的问题是“springModel数据存储在哪?”它存储在标准的java request范围中吗?答案是“是的”,从上边的输出可以看出,当handler方法执行的时候MyCommandBean是在model中,但是没有在request对象中。确实,spring不会把model数据作为request的属性,直到执行handler方法之后和下一个视图之前(在这个例子中是nextpage.jsp)

这也可以通过打印存储在index.jsp和nextpage.jsp中的HttpServletRequest中的数据展示出来,这两个页面我都使用jsp来展示HttpServletRequest的属性
[html]  view plain  copy
  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范围内没有属性
do something
Request Scope(key==values)
在这个例子中,当“do something”连接被点击的时候触发了MyController的handler方法执行,继而导致nextpage.jsp被执行,考虑到这是同样的jsp内容,再次提出不免有些啰嗦。当nextpage.jsp提出的时候,表明model的MyCommandBean在controller里被创建,并已经被加到HttpServletRequest范围中去了。这个spring model的属性key已经被复制,并且当做Request属性的key。
所以spring model数据的创建要早于handler方法的执行,在下一个视图加载前就已经被复制到HttpServletRequest中去了。

spring Model和Request后边的原因

你可能会疑惑为什么spring使用model属性,为什么不直接将数据加到Request对象中去呢,在Rod Johnson的书中我找到了这个问题的答案。下边就是来自这本书的关于model元素的文本。
直接给HttpServletRequest(或者Request属性)增加元素看起来实现了相同的目的,做这件事的原因很明确,当我们查看我们为MVC框架设置的要求的时候,应该尽可能使视图无关的,意味着视图技术不和HttpServletRequest绑定。

Spring的@SESSIONATTRIBUTE

现在你知道spring的model数据是如何管理的并且是如何和Httprequset属性关联的,那么spring的session数据呢?
spring的@SessionAttributes在controller上使用指定model属性应该存储在Session中。实际上,精确的讲spring开发文档已经表明了@SessionAttributes注解“列出了应该存储在Session中或者对话存储中model属性的名字”。
实际上,@SessionAttribute允许你做的是在加载视图之前,告诉spring你的哪一个model Attributes将会被复制到httpSession中去。
[java]  view plain  copy
  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. %>  

我在MyController中用@SessionAttributes做注解,来把同样的model属性加到spring Session中去。
[java]  view plain  copy
  1. @Controller  
  2. @SessionAttributes("myRequestObject")  
  3. public class MyController {  
  4.   ...  
  5. }  

我同样也在handler方法中增加了代码来展示什么属性在httpSession中
[java]  view plain  copy
  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注解后我们可以看到什么在session对象中,包括springMVC处理一个http 请求的前中后的过程里。结果如下,首先是index.jsp展示,我们可以看到在HttpServletRequest和httpSession中都没有属性数据。
do something
Request Scope(key == values)
Session Scope(key == values)
在handler方法执行过程中(HttpServletRequest),你可以看到MyCommandBean被加入到spring model属性中去,但是没有在HttpServletRequest和HttpSession范围中。
但是在handler方法执行之后并且nextpage.jsp被加载,你可以看到model的属性数据确实被作为属性复制到HttpServletRequest和httpSession中。

控制对话属性

现在你已经对spring model和Session属性数据是如何加载到HttpServletReq。uest和httpSession有很好的理解了。但你依旧对管理spring Session中的数据心生疑惑。spring提供了移除spring Session属性的方式,也可以在HttpSession中移除。
原文链接:http://www.intertech.com/Blog/understanding-spring-mvc-model-and-session-attributes/


+++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++

 今天需要针对预览功能将参数通过window.open(url+参数)的方式请求后台方法,进行页面跳转,然而当参数太大时,通过url的方式会导致请求参数过长而失败。所以只能改用post方式,将参数以bean或者requestbosy的方式传递给controller,但是这种方会使原来能自动跳转的url不跳转,目前还没找到原因。通过redirect的方式会导致塞到model的参数无法获取,因此需要将model参数存入到session中去。参考了以下两篇原文,供大家参考。

 

原文参考:http://blog.csdn.net/u012325167/article/details/52426523

              https://zhidao.baidu.com/question/476889704.html

今天遇到一个需求,在用户登陆之后,需要将其登陆状态保存到Session中。

我的逻辑是:用户登陆——用户登陆相关的Controller——验证完成之后,重定向到首页相关的Controller,进行相关信息的展示

在这个过程中,我在用户登陆成功后,利用RedirectAttributes将用户信息存入到其中,然后重定向到首页相关的Controller。但是之后遇到了一个问题:在展示数据的时候,第一次展示时,用户信息是存在的(也就是在刚刚重定向过来的时候),但如果这时候刷新页面,用户信息就消失了。这是因为我只把用户信息存在了RedirectAttributes中,RedirectAttributes之所以能在第一次显示,其实是利用了Session,它会在第一次跳转过来之后取到用户信息,然后再将Session中的用户信息删除掉,这就是刷新页面后信息消失的原因。

为了解决这个问题,我用到了@SessionAttributes。

方法是:

将@SessionAttributes注解到【首页相关的Controller】上,这样做的目的是:在用户验证完成后,重定向到【首页相关的Controller】时,将存放在Model中的指定内容存入Session中,这样以后的地方需要用到该数据时可以直接从Session中获取。

简单示例:

用户登陆的Controller中的验证方法:


@RequestMapping(value = "/login", method = {RequestMethod.POST})
public String login(String username, String password, RedirectAttributes model) {
   if ("xxx".equals(username) && "xxx".equals(password)) { model.addFlashAttribute("isAdmin", true); return "redirect:/"; } else { model.addFlashAttribute("errorMsg", "用户名或密码错误!"); return "redirect:/backend"; } }

 

在该Controller上注解@SessionAttributes,使得在调用该Controller时,将Model中的数据存入Session


@Controller
@RequestMapping("/")
@SessionAttributes("isAdmin") public class IndexController extends BasicController { //.... }

大功告成


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值