Saturday, April 26, 2014

Using Http Session with Spring based web applications

There are multiple ways to get hold of and use an Http session with a Spring based web application. This is a summarization based on an experience with a recent project.

Approach 1

Just inject in HttpSession where it is required.
@Service
public class ShoppingCartService {
 
 @Autowired 
 private HttpSession httpSession;
 
 ...
} 

Though surprising, since the service above is a singleton, this works well. Spring intelligently injects in a proxy to the actual HttpSession and this proxy knows how to internally delegate to the right session for the request.

The catch with handling session this way though is that the object being retrieved and saved back in the session will have to be managed by the user:

public void removeFromCart(long productId) {
 ShoppingCart shoppingCart = getShoppingCartInSession();
 shoppingCart.removeItemFromCart(productId);
 updateCartInSession(shoppingCart);
}

Approach 2

Accept it as a parameter, this will work only in the web tier though:

@Controller
public class ShoppingCartController {

  @RequestMapping("/addToCart")
  public String addToCart(long productId, HttpSession httpSession) {
    //do something with the httpSession 
  }

}


Approach 3
Create a bean and scope it to the session this way:

@Component
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS, value="session")
public class ShoppingCart implements Serializable{
...
}

Spring creates a proxy for a session scoped bean and makes the proxy available to services which inject in this bean. An advantage of using this approach is that any state changes on this bean are handled by Spring, it would take care of retrieving this bean from the session and propagating any changes to the bean back to the session. Further if the bean were to have any Spring lifecycle methods(say @PostConstruct or @PreDestroy annotated methods), they would get called appropriately.

Approach 4
Annotating Spring MVC model attributes with @SessionAttribute annotation:

@SessionAttributes("shoppingCart")
public class OrderFlowController {
 
 
 public String step1(@ModelAttribute("shoppingCart") ShoppingCart shoppingCart) {
  
 }
 
 public String step2(@ModelAttribute("shoppingCart") ShoppingCart shoppingCart) {
  
 }
 
 public String step3(@ModelAttribute("shoppingCart") ShoppingCart shoppingCart, SessionStatus status) {
  status.setComplete();
 } 

}

The use case for using SessionAttributes annotation is very specific, to hold state during a flow like above


Given these approaches, I personally prefer Approach 3 of using session scoped beans, this way depending on Spring to manage the underlying details of retrieving and storing the object into session. Other approaches have value though based on the scenario that you may be faced with, ranging from requiring more control over raw Http Sessions to needing to handle temporary state like in Approach 4 above.

6 comments:

  1. Never knew we could use it these many ways. Great post.

    ReplyDelete
  2. How can I access the bean in the approach 3 ?

    ReplyDelete
  3. How can I access the bean in approach 3 ?

    ReplyDelete
  4. Hi,

    What will happen if I access ShoppigCart using @Autowire in OrderFlowController ?

    ReplyDelete
  5. Do you know if Approach 3 will work with Spring Session (Redis or any other) ?

    ReplyDelete