I'm trying to undestand session beans in Spring. The book I'm reading says, about them, that:
the bean is created when needed and stored in the javax.servlet.http.HttpSession. When the session is destoyed so is the bean instance.
I tried with the following example:
The bean:
package com.at.test.web;
public class Cart {
public static int dummy = 0;
public Cart() {
System.out.println("Cart::<init> with hashCode " + hashCode());
}
}
The bean definition:
<beans:bean id="cartBean" class="com.at.test.web.Cart" scope="session">
<apo:scoped-proxy/>
</beans:bean>
The controller:
@Controller
public class HomeController {
@Autowired
private Cart cart;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpSession session, Model model) {
System.out.println("Cart is: " + cart.hashCode()
+ " ; dummy = " + (cart.dummy++)
+ " (" + cart.getClass().getCanonicalName() + ")"
+ "; session is: " + session.hashCode());
return "home.jsp";
}
}
This is what happens on Tomcat startup:
Cart::<init> with hashCode 970109301
I think Spring need this instance in order to create the CGLIB proxies. Anyway, I'm not that sure.
After the startup, I use two different browsers to have two different HttpSession. The result, when the controller is invoked, is:
Cart is: 578093288 ; dummy = 0 (com.at.test.web.Cart$$EnhancerByCGLIB$$2eb8334f); session is: 1013723725
Cart is: 578093288 ; dummy = 1 (com.at.test.web.Cart$$EnhancerByCGLIB$$2eb8334f); session is: 1060682497
The cart instance seems to be shared between HttpSession-s, but I was expecting two instances of Cart.
The same if I let the bean implements an interface, use annotation-driven approch and component scan:
public interface ICart {}
--
@Component
@Scope(value="session", proxyMode=ScopedProxyMode.INTERFACES)
public class Cart implements ICart {
public static int dummy = 0;
public Cart() {
System.out.println("Cart::<init> with hashCode " + hashCode());
}
}
Am I missing something? Did I misunderstood the meaning of session bean?
There's a lot of proxying and delegation going on.
This field
@Autowired
private Cart cart;
where Cart
is session scoped will be proxied as you can see in your logs
com.at.test.web.Cart$$EnhancerByCGLIB$$2eb8334f
But this proxy is not wrapping a Cart
object. It is wrapping a SimpleBeanTargetSource
which takes care of getting a Cart
bean from the BeanFactory
.
Your bean definition attaches a SessionScope
object which is responsible for checking your HttpSession
through a static
ThreadLocal
field in RequestContextHolder
. When request is made to get a bean from the BeanFactory
, it will delegate the action to the SessionScope
object which will check the HttpSession
, use the one there if it exists or create and register a new one if it doesn't.
You don't notice this with your test because
cart.hashCode()
delegates to the SimpleBeanTargetSource
object (incorrectly, if you ask me). But if you did
cart.toString();
you would see the difference as that actually reaches the underlying Cart
object.
Depending on the scope and proxying strategy you use, all this may be different, but the end goal is still achieved.