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
where
Cart
is session scoped will be proxied as you can see in your logsBut this proxy is not wrapping a
Cart
object. It is wrapping aSimpleBeanTargetSource
which takes care of getting aCart
bean from theBeanFactory
.Your bean definition attaches a
SessionScope
object which is responsible for checking yourHttpSession
through astatic
ThreadLocal
field inRequestContextHolder
. When request is made to get a bean from theBeanFactory
, it will delegate the action to theSessionScope
object which will check theHttpSession
, 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
delegates to the
SimpleBeanTargetSource
object (incorrectly, if you ask me). But if you didyou 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.