对于Spring的事务管理,我们一般只知道是采用AOP(面向切面)的方式进行事务管理。也就是说,在执行Service方法时,Spring会通过动态代理的方式去获取执行service方法的对象,然后在执行具体的业务方法之前和之后,可以加入spring的事务管理。
问题来了,既然要进行事务管理,spring必须要拿到service方法中使用的connection对象,在Hibernate中也就是session对象。本章节就为大家揭开Spring是如何获取到同一个connection对象的。这里我们拿HibernateTransactionManager来举例。
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
在service中的如何获取session对象,一般是用SessionFactory.getCurrentSession()。我们来看一下SessionFactory中的getCurrentSession()具体实现方法
private final transient CurrentSessionContext currentSessionContext;
public org.hibernate.classic.Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
可以看出返回的是从CurrentSessionContext中获取当前的session,再看其中一种实现方式ThreadLocalSessionContext。
private static final ThreadLocal context = new ThreadLocal();
public final Session currentSession() throws HibernateException {
Session current = existingSession( factory );
if (current == null) {
current = buildOrObtainSession();
// register a cleanup synch
current.getTransaction().registerSynchronization( buildCleanupSynch() );
// wrap the session in the transaction-protection proxy
if ( needsWrapping( current ) ) {
current = wrap( current );
}
// then bind it
doBind( current, factory );
}
return current;
}
private static Session existingSession(SessionFactory factory) {
Map sessionMap = sessionMap();
if ( sessionMap == null ) {
return null;
}
else {
return ( Session ) sessionMap.get( factory );
}
}
protected static Map sessionMap() {
return ( Map ) context.get();
}
上面的代码中有一个静态成员变量context,从这几个方法中大致可以看出,session实际上是从一个名为context的ThreadLocal静态变量中获取当前的线程中的session。假如是session不存在,则将产生一个新的Session然后在绑定到ThreadLocal中。
以上是调用SessionFactory.getCurrentSession()是怎么返回session的代码解读。然后再来读一下Spring的HibernateTransactionManager的代码。
找到doGetTransaction()方法,这个方法是获取事务管理器。其中可以看到假如sessionHolder为空,则获取session对象也是调用SessionFactory中的getCurrentSession()方法。
@Override
protected Object doGetTransaction() {
HibernateTransactionObject txObject = new HibernateTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" +
SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
//获取session
Session session = getSessionFactory().getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" +
SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
结论:在一个线程中,不论何时获取当前的Session都会是同于一个对象。
(注:ThreadLocal这里解释一下,是为每个线程都拷贝一份独立的对象,每个线程之间互不干扰。)
用一个流程图来表示一下: