天天看點

Spring 的@Transactional 如何開啟事務

java.lang.Object

  org.springframework.transaction.support.TransactionSynchronizationManager

public abstract class TransactionSynchronizationManager

extends Object

Central helper that manages resourcesand transaction synchronizations per thread.

為每個線程管理資源和事務的中心helper

 hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext時,

@Transactional,Spring的事務管理器HibernateTransactionManager.doBegin()方法開啟的Session和事務 就是綁定到TransactionSynchronizationManager的上下文(ThreadLocal的Map)中的。

 SpringSessionContext.currentSesssion()方法就是在TransactionSynchronizationManager的上下文中查找session。

配置:

<!-- 事務管理器 -->

    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <!--  注解驅動:解析@Transactional注解,對@Transactional注解的方法用指定的事務管理器實作事務管理  -->

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

上文回顧:

現在對于hibernate.current_session_context_class= org.springframework.orm.hibernate4.SpringSessionContext時的getCurrentSession()就很清楚了:

1:@Transactional聲明的方法執行時,Spring的TransactionManager會自動Open Sesion,自動開啟事務,并且将此Sesion綁定到SpringSessionContext(實際上是TransactionSynchronizationManager的上下文中)中。

2:SessionFactory.getCurrentSession()方法執行時,調用SpringSessionContext.currentSession()從TransactionSynchronizationManager的上下文中查找 目前的Session。

3:找到後傳回目前的Session,找不到,則傳回HibernateException("No Session found for current thread")

spring 的事務管理架構:

1.接口:PlatformTransactionManager。

該接口隻有三個方法:

(1)TransactionStatus getTransaction(TransactionDefinition definition)

  該方法的作用是:依據指定的事務定義傳回目前存在的事務或者是建立一個新的事務。這是入口。

(2)void commit(TransactionStatus status)

(3)void rollback(TransactionStatus status)

2.抽象類:public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable

該抽象類實作了接口PlatformTransactionManager定義的getTransaction(TransactionDefinition definition) 方法,并且是final類型的方法,

這就意味着具體的實作類都是用這個方法擷取事務的。方法内部調用doGetTransaction()方法用以擷取目前存在的事務,若是目前存在事務則取出并且依據指定的事務傳播行為處理。這個方法是抽象的,交給具體的事務管理器實作。若是目前不存在事務,則調用doBegin()方法建立一個事務并且綁定到TransactionSynchronizationManager的上下文(ThreadLocal的Map)中,綁定到目前線程。doBegin()方法也是抽象的,交給具體的事務管理器實作。

3.具體的事務管理器:

public class HibernateTransactionManager extends AbstractPlatformTransactionManager

implements ResourceTransactionManager, BeanFactoryAware, InitializingBean 

HibernateTransactionManager 實作了抽象類中的doGetTransaction()和doBegin()方法。

上述第一點是未驗證的,現在我們來分析一下源代碼:

[java]  view plain copy

  1. Public UserService  
  2. {  
  3.    @Transactional  
  4.    public void addUser(User user) throwsException  
  5.    {  
  6.       Session session =sessionFactory.getCurrentSession();  
  7.       session.save(user);  
  8.    }  
  9. }  

[java]  view plain copy

  1. //當然,聲明式事務管理實質也是用AOP實作的,此時取得的一個Proxy..  
  2. UserService userService = (UserService) beanFactory.getBean("userService");  
  3. User user = new User();  
  4. user.setName("Mark");  
  5. userService.addUser(user);  //在執行這一句時,應該是調用Proxy來開啟一個事務,并綁定到某個上下文中,我們跟進去看看  

先進入到JdkDynamicAopProxyimplements.invoke()方法

然後到ReflectiveMethodInvocation. proceed(),這裡調用TransactionInterceptor.invoke(this)方法: 

典型的攔截器模式:

1:按需開啟事務

2:遞歸執行下一個攔截器 或 執行代理目标方法

3:送出事務

[java]  view plain copy

  1. public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor,Serializable {  
  2.     public Object invoke(final MethodInvocationinvocation) throws Throwable {  
  3.                                                                                                                                                   .......  
  4.               //就在這一句開啟事務  
  5.               TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr,joinpointIdentification);  
  6.               ......  
  7.               retVal= invocation.proceed();   //執行下一個攔截器 或 執行代理目标的方法  
  8.               ......                                                                                                                              //送出事務                         
  9.               commitTransactionAfterReturning(txInfo);                                                                       
  10.               return retVal;  
  11.               .......  
  12.     }   
  13. }  

進入createTransactionIfNecessary(tm,txAttr, joinpointIdentification), 其中的tm.getTransaction(txAttr);    //這一句應該是取得事務的,跟進去

……………………………………….

AbstractPlatformTransactionManager.getTransaction()方法:

如果目前有事務,取出并根據事務傳播行為的配置去處理,如果目前沒有事務,調用doBegin開啟一個新事務

[java]  view plain copy

  1. public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {  
  2.  ......  
  3.    public final TransactionStatus getTransaction(TransactionDefinitiondefinition) throws TransactionException {  
  4.               //這裡是取得目前已綁定到TransactionSynchronizationManager的上下文的事務,主要為事務傳播行為而設  
  5.               Object transaction =doGetTransaction();  
  6.               ........  
  7.               //如果事務已存在,根據事務傳播行為來處理,  
  8.               if(isExistingTransaction(transaction)) {  
  9.                      // Existingtransaction found -> check propagation behavior to find out how to behave.  
  10.                      return handleExistingTransaction(definition, transaction, debugEnabled);  
  11.               }  
  12.               ......  
  13.               //這裡最關鍵,開啟一個新的事務,并放進TransactionSynchronizationManager的上下文中,并綁定到目前線程)  
  14.               doBegin(transaction,definition);  
  15.               return status;  
  16.               .......  
  17.        }  
  18. ......  
  19. }  

HibernateTransactionManager.doGetTransaction()方法:

嘗試擷取目前已綁定到TransactionSynchronizationManager的上下文的事務,主要為事務傳播行為而設

[java]  view plain copy

  1. public class HibernateTransactionManager extends AbstractPlatformTransactionManager  
  2.         implements ResourceTransactionManager, InitializingBean {  
  3.     protected Object doGetTransaction() {  
  4.         HibernateTransactionObject txObject = new HibernateTransactionObject();  
  5.         ......  
  6.         //在TransactionSynchronizationManager的上下文中查找目前的Session(實質上也是與線程綁定的)  
  7.         SessionHolder sessionHolder =  
  8.                 (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());  
  9.         ......  
  10.         return txObject;  
  11.     }  
  12. }  

HibernateTransactionManager.doBegin()方法:

開啟一個新的事務,并放進TransactionSynchronizationManager的上下文(ThreadLocal的Map)中,綁定到目前線程)

[java]  view plain copy

  1. public class HibernateTransactionManager extends AbstractPlatformTransactionManager  
  2.         implements ResourceTransactionManager, InitializingBean {  
  3. ......  
  4.      protected void doBegin(Object transaction, TransactionDefinition definition) {  
  5.                 HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;  
  6.         .........  
  7.         try {  
  8.             if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {  
  9.                 //這裡才真正的打開Session  
  10.                 Session newSession = SessionFactoryUtils.openSession(getSessionFactory());  
  11.                 .........  
  12.             }  
  13.             session = txObject.getSessionHolder().getSession();  
  14.                         .........  
  15.             Transaction hibTx;  
  16.             // Register transaction timeout.  
  17.             .........  
  18.             if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {  
  19.                 .........  
  20.             }  
  21.             else {  
  22.                 //這裡才真正地開啟事務  
  23.                 hibTx = session.beginTransaction();  
  24.             }  
  25.             .........  
  26.                         //如果這新開啟的Session,則将SessionHolder(Session和Transaction)放到TransactionSynchronizationManager的上下文中(綁定到目前線程)  
  27.             // Bind the session holder to the thread.  
  28.             if (txObject.isNewSessionHolder()) {                                                                                                          
  29.                                 //以鍵值對<SessionFactory,SessionHolder>形式綁定到TransactionSynchronizationManager的上下文中  
  30.                 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());  
  31.             }  
  32.             .......  
  33.         }  
  34.         .........  
  35.      }  
  36. ......  
  37. }  

Spring @Transactional 如何開啟事務 總結:

@Transactional聲明的方法執行時會調用AbstractPlatformTransactionManager.getTransaction()取得目前事務。

而getTransaction()的執行流程如下:

1:嘗試擷取目前已綁定到TransactionSynchronizationManager的上下文的事務

(調用HibernateTransactionManager.doGetTransaction()方法)

2:如果取得已存在的事務,則根據事務傳播屬性的設定來處理

(調用AbstractPlatformTransactionManager.handleExistingTransaction()方法)

3:如果沒有事務,則打開新的Session,開啟新的事務,并将該Session和事務綁定到TransactionSynchronizationManager的上下文中(調用HibernateTransactionManager.doBegin()方法)

-------------------------------------------------

核心還是了解TransactionSynchronizationManager,要懂得Spring的TransactionManager開啟事務後是以鍵值對<SessionFactory,SessionHolder>形式 存放到 TransactionSynchronizationManager的上下文(ThreadLocal的Map)中(以ThreadLocal的方式與目前線程綁定).

繼續閱讀