Spring MVC Model and Session Attributes
As a Java Web application developer, you quickly learn about the request (HttpServletRequest) and session (HttpSession) scopes. Understanding these scopes and how to work data and objects in and out of these scopes is critical to designing and building Web applications in Java. [For a quick tutorial on request and session scopes, here is a post in StackOverflow that can help.]
Spring MVC Scopes
When I started writing Web applications in Spring MVC, I found the Spring model and session attributes to be a bit of a mystery – especially as they relate to the HTTP request and session scopes and their attributes that I knew well. Was a Spring model element going to be found in my session or request? If so, how could I control this? In this post, I hope to demystify how Spring MVC’s model and session work.
Spring’s @ModelAttribute
There are several ways to add data or objects to Spring’s model. Data or objects are typically added to Spring’s model via an annotated method in the controller. In the example below, @ModelAttribute is used to add an instance of MyCommandBean to the model under the key of “myRequestObject”.
@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 }
On an incoming request, any methods annotated with @ModelAttribute are called before any controller handler method (like requestHandlingMethod in the example above). These methods add data to a java.util.Map that is added to the Spring model before the execution of the handler method. This can be demonstrated by a simple experiment. I created two JSP pages: index.jsp and nextpage.jsp. A link on index.jsp page is used to send a request into the application triggering the requestHandlingMethod() of MyController. Per the code above, the requestHandlingMethod() returns “nextpage” as the logical name of the next view which is resolved to nextpage.jsp in this case.
When this little Web site is executed in this fashion, the System.out.println’s of the controller, show how the @ModelAttribute method is executed before the handler method. It also shows that the MyCommandBean was created and added to Spring’s model and was available in the 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
Now, the question is “where is Spring model data stored?” Is it stored in the standard Java request scope? The answer is – yes… eventually. As you can tell from the output above, MyCommandBean is in the model, but not yet in the request object when the handler method executes. Indeed, Spring does not add the model data to the request as an attribute until after the execution of the handler method and before presentation of the next view (in this case the nextpage.jsp).
This can also be demonstrated by printing out the attribute data stored in the HttpServletRequest in both index.jsp and nextpage.jsp. I arranged for both of these pages to use a JSP scriptlet (shown below) to display the HttpServletRequest attributes.
<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)); %><br /> <% } %>
When the application comes up and index.jsp is displayed, you can see that there are no attributes in request scope.
In this case, when the “do something” link is clicked it causes the MyController’s handler method to execute, which in turn causes the nextpage.jsp to be displayed. Given the same JSP scriptlet is on the nextpage.jsp, it too renders what is in the request scope. Lo and behold, when nextpage.jsp renders, it shows the model MyCommandBean created in the controller has been added to the HttpServletRequest scope! The Spring model attribute key of “myRequestObject” has even been copied and used as the request attribute’s key.
So Spring model data created prior to (or during) the handler method execution has been copied to the HttpServletRequest before the next view is rendered.
Reason Behind Spring Model Versus Request
You may wonder why Spring uses model attributes. Why not just add data directly to the request object? I found the answer to this question in Rod Johnson et. al’s book Professional Java Development with the Spring Framework. This book is a little dated with regard to the Spring API (written to Spring 2.0), but I have always found the text provide a little more explanation of what’s going on under the covers of the Spring engine. Here is the quote from that text regarding model elements:
“…adding elements directly to the HttpServletRequest (as request attributes) would seem to serve the same purpose. The reason to do this is obvious when taking a look at one of the requirements we have set for the MVC framework: It should be as view-agnostic as possible, which means we’d like to be able to incorporate view technologies not bound to the HttpServletRequest as well.” (page 429-430)
Spring’s @SessionAttributes
So now you know how Spring’s model data is managed and how it relates to regular Http request attribute data. What about Spring’s session data?
Spring’s @SessionAttributes is used on a controller to designate which model attributes should be stored in the session. Actually, to be precise, the Spring documentation states that the @SessionAttributes annotation “list the names of model attributes which should be transparently stored in the session or some conversational storage.” Again, the “some conversational storage” suggests how Spring MVC tries to remain technology agnostic it is design.
In actually, what @SessionAttributes allows you to do is tell Spring which of your model attributes will also be copied to HttpSession before rendering the view. Again, this can be demonstrated with a little code.
In my index.jsp and nextpage.jsp, I added an additional JSP scriptlet to show the 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)); %><br /> <% } %>
I annotated MyController with @SessionAttributes to put the same model attribute (myRequestObject) in Spring session.
@Controller @SessionAttributes("myRequestObject") public class MyController { ... }
I also added code to the handler method of my controller to show what attributes are in HttpSession (just as it shows what attributes are in HttpServletRequest).
@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"; }
So now, we should be able to see what is in the session object before, during, and after Spring MVC has handled one HTTP request when annotated with @SessionAttributes. The results are shown below. First, as the index.jsp page is displayed (before the request is sent and handled by Spring MVC), we see that there is no attribute data in either the HttpServletRequest or HttpSession.
During the execution of the handler method (requestHandlingMethod), you see MyCommandBean has been added to the Spring model attributes, but it is not yet in the HttpServletRequest or HttpSession scope.
But after the handler method has executed and when the nextpage.jsp is rendered, you can see that the model attribute data (MyCommandBean) has indeed been copied as an attribute (with the same attribute key) to both HttpServletRequest and HttpSession.
Controlling Session Attributes
So now you have an appreciation of how Spring model and session attribute data are added to HttpServletRequest and HttpSession. But now you may be concerned with how to manage that data in Spring session. Spring provides a means to remove Spring session attributes, and thereby also remove it from HttpSession (without having to kill the entire HttpSession). Simply add a Spring SessionStatus object as a parameter to a controller handler method. In this method, use the SessionStatus object to end the Spring session.
@RequestMapping("/endsession") public String nextHandlingMethod2(SessionStatus status){ status.setComplete(); return "lastpage"; }
Wrap Up
So hopefully, this post has helped you understand Spring model and session attributes. Its not magic, its just a matter of understanding how HttpSession and HttpServletRequest are used to store Spring model and session attributes.
Huge! you got featured in the Spring blog!
Thank you, great explanation!
Thank You!!!!
this is great article, it really helped me
sollra dae
Excellent write-up. Thank you.
Thanks… Its wonderfullll…. clear and concise:)
what’s the use case ?When you are using @model att and @sessionattr for same purpose as there is request scope available for forward mode to jsp,what is the use of session
bravo , you are good. 23333333 666666666666
i want to handle multiple user using sessions with spring framework a bus how should i do it (i.e.if wat the user to select a particular seat for booking)
I am haveing one basic doubt in spring mvc. How a controller manages more requests at a time.How many instances will be created. whether we configure any scope to a controller
Please giveme your answers please..Please write mail on madhusitams004@gmail.com
Hello Jim,
Can you please elaborate this quote
“…adding elements directly to the HttpServletRequest (as request attributes) would seem to serve the same purpose. The reason to do this is obvious when taking a look at one of the requirements we have set for the MVC framework: It should be as view-agnostic as possible, which means we’d like to be able to incorporate view technologies not bound to the HttpServletRequest as well.” (page 429-430)
I do get the literal meaning but still not able to understand how adding object to HttpRequest is not view agnostic ? Thank You.
Great stuff, thank you for doing the legwork on this and explaining it so well.