This question based on one fact: instance of javax.persistence.EntityManager is NOT thread-safe. Then how does spring handle concurrency on singleton DAO object.
Suppose we have a simple DAO bean looks like below, used in a concurrent scenario, such as in a web application.
import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; import com.shengwang.demo.model.User; // a trivial entity @Transactional public class UserDao { @PersistenceContext private EntityManager em; public void addUser (User user) { em.persist(user); } }
Can you set the scope of this bean to singleton in JEE context? (NO)
Can you set the scope of this bean to singleton in Spring context? (YES, but why? see below)
1. In JEE
Usually DAO bean is stateless. Our DAO bean above is just a stateless bean, so in JEE most common way is to mark it as @Stateless and create a instance pool of this DAO. JEE container will maintain this pool, assign a bean instance handle for every individual invocation, and bean will release back to the pool after invocation.
Because the EntityManager is non thread-safe, the solution for concurrency in JEE is creating a pool. That sounds reasonable.
2. In Spring
The default scope of bean in spring is singleton, and offical document recommend you to set DAO bean singleton, which means just use the default scope config is fine.
But Why? When multiple threads access this single object, why the non thread-safe EntityManager instance does not complain? The reason is that in Spring framework, the Entitymanager instance em in the Dao bean is not a real EntityManager, but a proxy. Which mean very invocation on em, like em.persist(), is handled by a proxy.
In case you are not familiar with Proxy in Java reflection, here are some basic knowledge for quick understanding.
2.1 basic about proxy
In java reflection package java.lang.reflect , there is a Proxy class. Java provide a mechenism to create a proxy for any class. Suppose we want to create a proxy for class Foo. Code looks like :
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
The instance f is a proxy. The last parameter handler is an implementation of interface java.lang.reflect.InvocationHandler. When any methods of instance f get called, the handler’s only method , invoke(Object proxy, Method method, Object[] args), get called. So in this method, you have a chance to place some logic before/after the real invocation.
2.2 Spring use proxy to get real EntityManager on every invocation.
Since the em injected to DAO is just a proxy, every call on the em will first try to get the real EntityManager object in the handler. This logic is located in org.springframework.org.jpa.SharedEntityManagerCreator. This class implements the InvocationHandler interface and has a invoke() method. In this method, there are codes like:
EntityManager target = EntityManagerFactoryUtils.doGetTransactionalEntityManager(...);
This doGetTransactionalEntityManager(…) will get EntityManager bound to current thread! If we follow the doGetTransactionalEntityManager(…) method, we will find following in method.
EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
Keep tracking getResource(…), you will found the resource is a ThreadLocal map variable defined in class org.springframework.transaction.support.TransactionSynchronizationManager
Now you should understand why the Dao can (should) be a singleton bean in spring framework. Because spring internally use Proxy and ThreadLocal to eliminate the impact the non thread-safe EntityManager bring to stateless bean. No need to create pool for concurrency anymore!
All code snippets above are based on Spring framework 4.1.0.RELEASE.
3. Recap
To some extends, you can think spring framework use a ThreadLocal variable as the stateless bean pool in JEE. You can also think this somehow way of a Flyweight design pattern, use a shared object to save overhead of create/destroy objects.
0 comments:
Post a Comment