Spring mvc Session

Using Sessions in Spring-MVC (including "scoped-proxies")

 

 

On the Spring-MVC video training course, I described three different approaches to handling sessions in Spring.

On the video, I mention that there is also a fourth way, but since the course was getting a bit long I said that I would cover this in a blog post, and here it is.

Thankyou to Bob Casazza for reminding me to do this.

First, a recap of the three approaches described on the video:

1: Use HttpSession directly.

With this approach, you declare HttpSession as a parameter to your controller method. The example on the course looks like this:

public ModelAndView addToCart( @RequestParam ( "id" ) int id, HttpSession session)
{
    ShoppingCart cart = (ShoppingCart)session.getAttribute( "cart" );
    // etc, continue with the cart
}
  

Pros: it's simple, very much like you would do it in older Spring-MVC and other less capable frameworks.

Cons: it's messy, exposes your clean controller to the Servlet API and needs null checking after you've called getAttribute. Unit testing of your controller is now much harder to do (you need to Mock the HttpSession object).

I don't like this approach, I would avoid it unless necessary (later in the post, I'll explain when I would use it).

2: Scope the Controller

Make your controller session scoped. You can then simply instantiate the object you want to store in session scope as a member variable of the controller...

@Controller
@Scope ( "session" )
public class CartManagementController
{
    private ShoppingCart cart = new ShoppingCart();
 
    @RequestMapping ( "/addToCart" )
    public ModelAndView addToCart( @RequestParam ( "id" ) int id)
    {
       // now just use the cart
    }
}

Pros: A very clean controller, very unit testable.

Cons: A new controller is created for each session, the controller object must be stored in HttpSession. This could bloat the session and in particular could mean replication problems on a large scale system. (Replication: where your web application is hosted on multiple servers. Then the session has to be copied from one server to another. Whilst this is automatic, big sessions cause serious performance problems)

3: Scope the Objects in the Session

This is a narrowing of the session scope, and we session scope just the object we want to store in the session.

@Component
@Scope ( "session" )
public class ShoppingCart
{
    // just a plain java class - member variables and methods as usual
}
 
 

Note that the class is now a Spring Bean.

Then, we inject instances of the class into the controller:

@Controller
@Scope ( "request" )
public class CartManagementController
{
    @Autowired
    private ShoppingCart cart;
 
    @RequestMapping ( "/addToCart" )
    public ModelAndView addToCart( @RequestParam ( "id" ) int id)
    {
       // now just use the cart
   
}

So, for each request, Spring creates an instance of the controller and then finds the shopping cart from the session.

Crucually, the controller in this approach MUST be request scoped. The default is for Spring to create a global singleton instance of the controller, and this would not work as a singleton is shared by all requests (you can't injection session scoped objects into singleton scoped objects anyway).

Pros: Clean testable controller as in approach two, with the added benefit of the session now only holds the relevant session data.

Cons: A new instance of the controller is created for each request. This is fine if the controller is "small", but if it is expensive to create (ie the constructor is slow for some reason), scalability would be a problem. Also, this approach is harder to understand because of the request scoped controller.

Ok, so they're the three approaches on the course. The problem is they all have drawbacks. I personally almost always use approach 3 where possible, but if I have a "heavy weight" controller, I'd consider using approach 1.

But the fourth approach removes all of the downsides of the previous. The only "con" of this approach is that it is much more complicated. It relies on Spring's best friend: proxies...

4: Use a <aop:scoped-proxy/>

This is covered in full in the Spring Reference manual (at the time of writing, at http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/beans.html#beans-factory-scopes-other-injection)

The general idea of this approach is that you declare your session data as a regular spring bean, with a special tag applied to it (<scoped-proxy>). Your controller will remain a regaular Spring bean, as singleton scope.

With the scoped-proxy tag, your controller looks like it is holding a reference to the session data, but it is actually holding a reference to a proxy which Spring has generated at run time. This proxy's responsibility is to find a session each time it is accessed.

If this is a bit complicated, you might need to check out our AOP session in the Spring Fundamentals video. Or, you can just copy what's here:

Sadly, they haven't created an annotation for scoped-proxy, so your session data (the shopping cart) has to be declared in old-school XML. You add this to your Spring wiring (eg Dispatcher-servlet.xml on the course):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
      xsi:schemaLocation="http://www.springframework.org/schema/beans
 
    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    < bean id = "shoppingCart" class = "com.virtualpairprogrammers.ShoppingCart" scope = "session" >
       <!-- this next element effects the proxying of the surrounding bean -->
       < aop:scoped-proxy />
    </ bean >
</ beans >


Now, your controller looks very simple:

?
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class CartManagementController
{
    @Autowired
    private ShoppingCart cart;
 
    @RequestMapping ( "/addToCart" )
    public ModelAndView addToCart( @RequestParam ( "id" ) int id)
    {
       // now just use the cart
   
}

Pros: unit testable and clean as before, only session data is stored in the HttpSession, and the controller is a single-instance global singleton, so no issues with performance of creating them.

Cons: it's much harder to understand (I've taken hours over this post!) and you have to fall back to old fashioned XML wiring.

Conclusion:

The "fourth approach" is probably the most elegant in that it solves all of the technical problems identified earlier. But really, in most situations, approach 3 will be just fine. I have never felt the need to use this fourth approach, it seems like a really heavy solution to the session "problem".

In real life, I've always been happy to use approach 3, and when I'm worried about performance (in the rare case where I'm doing heavy work in the constructor), I'll use approach 1 instead.

I hope no-one minds me omitting approach four from the course, I felt that we had more than enough information on sessions - but I'm glad I've been able to cover it here.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值