xhchc

          危波帆墻,笑談只在桃花上;與誰共尚,風(fēng)吹萬里浪; 相依相偎,不做黃泉想;莫惆悵,碧波潮生,一蕭自狂放……

           

          Spring聲明式事務(wù)管理源碼解讀之事務(wù)開始(轉(zhuǎn))

          Spring聲明式事務(wù)管理源碼解讀

          簡介:事務(wù)是所有企業(yè)應(yīng)用系統(tǒng)的核心,之前人們使用ejb的時候,容器事務(wù)管理(CMT),是slsb最令人稱道的地方,據(jù)說很多人使用ejb,使用slsb就是為了cmt,但是spring出現(xiàn)之后,格局就變了,因?yàn)槌绦騿T又多了一種選擇,就是聲明式事務(wù)管理,聲明式事務(wù)管理是基于AOP的,及AOP是它的底層特性,本文的目的就是為了和大家探討一下spring的聲明式事務(wù)管理,從源代碼來分析它的背后的思想。(謝謝異常的建議,因?yàn)楸疚脑瓉頉]有簡介


          這個是我昨天在解決問題是看源碼得一點(diǎn)體驗(yàn),可能說得比較大概,希望大家多多討論,把本貼得質(zhì)量提高上去,因?yàn)閟pring實(shí)現(xiàn)的事務(wù)管理這部分我相信還是有點(diǎn)復(fù)雜的。一個人未必能想得十分清楚
          在spring的聲明式事務(wù)管理中,它是如何判定一個及標(biāo)記一個方法是否應(yīng)該是處在事務(wù)體之中呢。

          首先要理解的是spring是如何來標(biāo)記一個方法是否應(yīng)該處在事務(wù)體之中的。有這樣一個接口TransactionDefinition,其中定義了很多常量,它還有一個子接口TransactionAttribute,其中只有一個方法rollback。
          TransactionDefinition中有很多常量定義,它們分別屬于兩種類型,傳播途徑和隔離級別
          Java代碼 復(fù)制代碼
          1. /**  
          2.      * Support a current transaction, create a new one if none exists.  
          3.      * Analogous to EJB transaction attribute of the same name.  
          4.      * <p>This is typically the default setting of a transaction definition.  
          5.      */  
          6.     int PROPAGATION_REQUIRED = 0;  
          當(dāng)然其中也定義了隔離級別
          /**
          Java代碼 復(fù)制代碼
          1. * A constant indicating that dirty reads are prevented; non-repeatable reads   
          2.  * and phantom reads can occur. This level only prohibits a transaction   
          3.  * from reading a row with uncommitted changes in it.   
          4.  * @see java.sql.Connection#TRANSACTION_READ_COMMITTED   
          5.  */   
          6. int ISOLATION_READ_COMMITTED   = Connection.TRANSACTION_READ_COMMITTED;  
          同時還有兩個對應(yīng)的方法來得到這樣的傳播途徑和隔離級別
          Java代碼 復(fù)制代碼
          1. /**  
          2.      * Return the propagation behavior.  
          3.      * Must return one of the PROPAGATION constants.  
          4.      * @see #PROPAGATION_REQUIRED  
          5.      * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()  
          6.      */  
          7.     int getPropagationBehavior();   
          8.   
          9.     /**  
          10.      * Return the isolation level.  
          11.      * Must return one of the ISOLATION constants.  
          12.      * <p>Only makes sense in combination with PROPAGATION_REQUIRED or  
          13.      * PROPAGATION_REQUIRES_NEW.  
          14.      * <p>Note that a transaction manager that does not support custom  
          15.      * isolation levels will throw an exception when given any other level  
          16.      * than ISOLATION_DEFAULT.  
          17.      * @see #ISOLATION_DEFAULT  
          18.      */  
          19.     int getIsolationLevel();  
          這個接口有一個默認(rèn)的實(shí)現(xiàn)DefaultTransactionDefinition。然后它還有子類,比如說
          DefaultTransactionAttribute。Spring在判斷一個方法是否需要事務(wù)體的時候其實(shí)是創(chuàng)建一個TransactionAttribute實(shí)現(xiàn)的實(shí)例.

          有了上面的簡單介紹就可以進(jìn)入真正判斷是否需要事務(wù)的地方了。這個方法在TransactionAspectSupport類里,


          Java代碼 復(fù)制代碼
          1. /**  
          2.      * Create a transaction if necessary.  
          3.      * @param method method about to execute  
          4.      * @param targetClass class the method is on  
          5.      * @return a TransactionInfo object, whether or not a transaction was created.  
          6.      * The hasTransaction() method on TransactionInfo can be used to tell if there  
          7.      * was a transaction created.  
          8.      */  
          9.     protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) {   
          10.         // If the transaction attribute is null, the method is non-transactional.   
          11.         final TransactionAttribute sourceAttr =   
          12.                 this.transactionAttributeSource.getTransactionAttribute(method, targetClass);//就是在這里判斷了這個方法的事務(wù)屬性   
          13.         TransactionAttribute txAttr = sourceAttr;   
          14.   
          15.         // If no name specified, apply method identification as transaction name.   
          16.         if (txAttr != null && txAttr.getName() == null) {   
          17.             final String name = methodIdentification(method);   
          18.             txAttr = new DelegatingTransactionAttribute(sourceAttr) {   
          19.                 public String getName() {   
          20.                     return name;   
          21.                 }   
          22.             };   
          23.         }   
          24.   
          25.         TransactionInfo txInfo = new TransactionInfo(txAttr, method);   
          26. //TransactionInfo是TransactionAspectSupport的一個內(nèi)部類,它的主要功能是記錄方法和對應(yīng)的事務(wù)屬性   
          27.         if (txAttr != null) {   
          28.             // We need a transaction for this method   
          29.             if (logger.isDebugEnabled()) {   
          30.                 logger.debug("Getting transaction for " + txInfo.joinpointIdentification());   
          31.             }   
          32.   
          33.             // The transaction manager will flag an error if an incompatible tx already exists   
          34.             txInfo.newTransactionStatus(this.transactionManager.getTransaction(txAttr));//這個方法要仔細(xì)的看   
          35.         }   
          36.         else {   
          37.             // The TransactionInfo.hasTransaction() method will return   
          38.             // false. We created it only to preserve the integrity of   
          39.             // the ThreadLocal stack maintained in this class.   
          40.             if (logger.isDebugEnabled())   
          41.                 logger.debug("Don't need to create transaction for [" + methodIdentification(method) +   
          42.                         "]: this method isn't transactional");   
          43.         }   
          44.   
          45.         // We always bind the TransactionInfo to the thread, even if we didn't create   
          46.         // a new transaction here. This guarantees that the TransactionInfo stack   
          47.         // will be managed correctly even if no transaction was created by this aspect.   
          48.         txInfo.bindToThread();   
          49.         return txInfo;   
          50.     }  

          TransactionInfo是TransactionAspectSupport的一個內(nèi)部類,它的主要功能是記錄方法和對應(yīng)的事務(wù)屬性,在上面這個方法的最后,這個TransactionInfo對象被保存到當(dāng)前線程中。

          而這個方法會在事務(wù)攔截器TransactionInterceptor中被調(diào)用,TransactionInterceptor實(shí)際上是TransactionAspectSupport的子類,看看其中的invoke方法:
          Java代碼 復(fù)制代碼
          1. // Work out the target class: may be <code>null</code>.   
          2.         // The TransactionAttributeSource should be passed the target class   
          3.         // as well as the method, which may be from an interface   
          4.         Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null;   
          5.            
          6.         // Create transaction if necessary.   
          7.         TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);   
          8.   
          9.         Object retVal = null;   
          10.         try {   
          11.             // This is an around advice.   
          12.             // Invoke the next interceptor in the chain.   
          13.             // This will normally result in a target object being invoked.   
          14.             retVal = invocation.proceed();   
          15.         }   
          16.         catch (Throwable ex) {   
          17.             // target invocation exception   
          18.             doCloseTransactionAfterThrowing(txInfo, ex);   
          19.             throw ex;   
          20.         }   
          21.         finally {   
          22.             doFinally(txInfo);   
          23.         }   
          24.         doCommitTransactionAfterReturning(txInfo);//在這里執(zhí)行方法結(jié)束之后需要的操作   
          25.         return retVal;  

          這個方法就如同一般的interceptor需要實(shí)現(xiàn)的方法一樣。只不過在這個方法里判斷被反射的方法是否需要事務(wù)。

          接著我們重點(diǎn)再回頭看一下createTransactionIfNecessary方法里的這一句:
          txInfo.newTransactionStatus(this.transactionManager.getTransaction(txAttr));
          接著我們就應(yīng)該去看看這個getTransaction方法了,假設(shè)我們是使用hibernate3,其他類似。看getTransaction之前我們來看一下這兩類和一個接口
          接口PlatformTransactionManager
          抽象類public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager
          類public class HibernateTransactionManager extends AbstractPlatformTransactionManager,很明顯,這里有一個方法模板模式。
          那我們看一下AbstractPlatformTransactionManager中得getTransaction方法:
          Java代碼 復(fù)制代碼
          1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {   
          2.         Object transaction = doGetTransaction();//抽象方法,也需要子類實(shí)現(xiàn),這個方法同樣很重要   
          3.   
          4.         // Cache debug flag to avoid repeated checks.   
          5.         boolean debugEnabled = logger.isDebugEnabled();   
          6.         if (debugEnabled) {   
          7.             logger.debug("Using transaction object [" + transaction + "]");   
          8.         }   
          9.   
          10.         if (definition == null) {   
          11.             // Use defaults if no transaction definition given.   
          12.             definition = new DefaultTransactionDefinition();   
          13.         }   
          14.   
          15.         if (isExistingTransaction(transaction)) {   
          16.             // Existing transaction found -> check propagation behavior to find out how to behave.   
          17.             return handleExistingTransaction(definition, transaction, debugEnabled);   
          18.         }   
          19.   
          20.         // Check definition settings for new transaction.   
          21.         if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {   
          22.             throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());   
          23.         }   
          24.   
          25.         // No existing transaction found -> check propagation behavior to find out how to behave.   
          26.         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {   
          27.             throw new IllegalTransactionStateException(   
          28.                     "Transaction propagation 'mandatory' but no existing transaction found");   
          29.         }   
          30.         else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||   
          31.                 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||   
          32.             definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   
          33.             if (debugEnabled) {   
          34.                 logger.debug("Creating new transaction with name [" + definition.getName() + "]");   
          35.             }   
          36.             doBegin(transaction, definition);   
          37.             boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);   
          38.             return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);   
          39.         }   
          40.         else {   
          41.             // Create "empty" transaction: no actual transaction, but potentially synchronization.   
          42.             boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);   
          43.             return newTransactionStatus(definition, nullfalse, newSynchronization, debugEnabled, null);   
          44.         }   
          45.     }  
          上面的代碼很多地方都有解釋,所以很好理解,這段代碼的關(guān)鍵部分在doBegin(transaction,definition)這里(這是一個抽象方法,子類必須實(shí)現(xiàn)這個方法,
          具體依賴于抽象,這個是對方法模板模式的一個概括。),前面講到我們假設(shè)是使用hibernate,那么就看看HibernateTransactionManager這個類吧,doBegin里的參數(shù)1,transaction其實(shí)是HibernateTransactionObject的一個實(shí)例,這個實(shí)例里主要存放的就是sessionholder,sessionholder里存放的就是開始事務(wù)的session和transaction對象,如果之前沒有sessionholder存放到線程中,那么這個HibernateTransactionObject的實(shí)例的屬性其實(shí)是空的,這一點(diǎn)可以在doBegin方法的實(shí)現(xiàn)中看出來
          Java代碼 復(fù)制代碼
          1. protected void doBegin(Object transaction, TransactionDefinition definition) {   
          2.         if (getDataSource() != null && TransactionSynchronizationManager.hasResource(getDataSource())) {   
          3.             throw new IllegalTransactionStateException(   
          4.                     "Pre-bound JDBC Connection found - HibernateTransactionManager does not support " +   
          5.                     "running within DataSourceTransactionManager if told to manage the DataSource itself. " +   
          6.                     "It is recommended to use a single HibernateTransactionManager for all transactions " +   
          7.                     "on a single DataSource, no matter whether Hibernate or JDBC access.");   
          8.         }   
          9.   
          10.         Session session = null;   
          11.   
          12.         try {   
          13.             HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;   
          14.             if (txObject.getSessionHolder() == null) {   
          15.                 Interceptor entityInterceptor = getEntityInterceptor();   
          16.                 Session newSession = (entityInterceptor != null ?   
          17.                         getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());   
          18.                 if (logger.isDebugEnabled()) {   
          19.                     logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");   
          20.                 }   
          21.                 txObject.setSessionHolder(new SessionHolder(newSession), true);  
          }//我們看到,如果傳進(jìn)來的transaction中并沒有存放sessionholder,那么就新建一個session,放到新的sessionholder中,再放到HibernateTransactionObject的實(shí)例中去,順便說一下,這個變量的名字取得真是差,雖然是Juergen Hoeller寫的,也要批一下,搞得別人會以為是Transaction的實(shí)例

          Java代碼 復(fù)制代碼
          1. txObject.getSessionHolder().setSynchronizedWithTransaction(true);   
          2.             session = txObject.getSessionHolder().getSession();   
          3.   
          4.             Connection con = session.connection();   
          5.             Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);   
          6.             txObject.setPreviousIsolationLevel(previousIsolationLevel);   
          7.   
          8.             if (definition.isReadOnly() && txObject.isNewSessionHolder()) {   
          9.                 // Just set to NEVER in case of a new Session for this transaction.   
          10.                 session.setFlushMode(FlushMode.NEVER);   
          11.             }//如果是只讀事務(wù),并且sessionholder是新建的,那么就設(shè)置hibernate的flushmode為never   
          12.   
          13.             if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {   
          14.                 // We need AUTO or COMMIT for a non-read-only transaction.   
          15.                 FlushMode flushMode = session.getFlushMode();   
          16.                 if (FlushMode.NEVER.equals(flushMode)) {   
          17.                     session.setFlushMode(FlushMode.AUTO);   
          18. //如果session的flushmode是nerver,就設(shè)置為auto,因?yàn)槿绻聞?wù)定義成非readonly,那么這個session一定是可以flush的   
          19.                     txObject.getSessionHolder().setPreviousFlushMode(flushMode);   
          20.                 }   
          21.             }   
          22.   
          23.             // Add the Hibernate transaction to the session holder.   
          24.             txObject.getSessionHolder().setTransaction(session.beginTransaction());//開始一個事務(wù),并把這個事務(wù)對象放到sessionholder中,隨后這個sessionholder會通過threadlocal放到線程中,以供在commit時使用   
          25.   
          26.             // Register transaction timeout.   
          27.             if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {   
          28.                 txObject.getSessionHolder().setTimeoutInSeconds(definition.getTimeout());//設(shè)置超時時間,如果其超時時間為-1,則不進(jìn)行設(shè)置,如果不是-1,那么超時時間是這樣設(shè)置的new Date(System.currentTimeMillis() + millis*1000);既程序員在配置文件中指定的其實(shí)是秒數(shù)   
          29.             }   
          30.   
          31.             // Register the Hibernate Session's JDBC Connection for the DataSource, if set.   
          32.             if (getDataSource() != null) {   
          33.                 ConnectionHolder conHolder = new ConnectionHolder(con);   
          34.                 if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {   
          35.                     conHolder.setTimeoutInSeconds(definition.getTimeout());   
          36.                 }   
          37.                 if (logger.isDebugEnabled()) {   
          38.                     logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");   
          39.                 }   
          40.                 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);   
          41.                 txObject.setConnectionHolder(conHolder);   
          42.             }   
          43.   
          44.             // Bind the session holder to the thread.   
          45.             if (txObject.isNewSessionHolder()) {   
          46.                 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());//如果是新的sessionholder則綁定到線程。這樣在進(jìn)入方法棧中的下一個方法時就能得到整個sessionholder了,connectionholder亦是如此   
          47.             }   
          48.         }   
          49. catch (Exception ex) {   
          50.             SessionFactoryUtils.releaseSession(session, getSessionFactory());//如果拋出異常就釋放這個session,這個操作還會在后面出現(xiàn)   
          51.             throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);   
          52.         }   
          53.     }  
          通過以上對代碼的注釋可以知道,如果給service設(shè)置聲明式事務(wù)管理,假設(shè)事務(wù)傳播途徑為required,然后一個service調(diào)用另一個service時,他們其實(shí)是共用一個session,原則是沒有就創(chuàng)建,有就不創(chuàng)建,并返回之前已創(chuàng)建的session和transaction。也就是說spring通過threadlocal把session和對應(yīng)的transaction放到線程之中,保證了在整個方法棧的任何一個地方都能得到同一個session和transaction。
          所以如果你的方法在事務(wù)體之內(nèi),那么你只要通過hibernatesupportdao或者h(yuǎn)ibernatetemplate來得到session的話,那這個session一定是開始事務(wù)的那個session,這個得到session的主要方法在SessionFactoryUtils里,我們來看一下
          (這里還有一個小細(xì)節(jié),public abstract class SessionFactoryUtils ,Juergen Hoeller在寫工具類的時候?yàn)榱瞬荒茏屍溆袑?shí)例使用的是abstract,而我們一般的做法是final類加private的構(gòu)造方法,看上去不怎么雅觀,看看源代碼還是能學(xué)習(xí)到不少寫代碼的技巧的,這里還有一個插曲,上次feiing還說java為什么不能弄成final和abstract同時存在呢,這樣就可以確保既不會有實(shí)例產(chǎn)生,也不能繼承了,呵呵)
          在SessionFactoryUtils的doGetSession里寫到,如果當(dāng)前線程有綁定session,則返回這個session,如果沒有綁定session,則看是否允許創(chuàng)建(既allowCreate這個參數(shù)是true還是false,這個參數(shù)將會在很多地方設(shè)計(jì)到,比如說hibernatetemplate和hibernatedaosupport里都有),如果不允許創(chuàng)建就拋出一個原始的hibernateException,舉個例子,如果你沒有給某個service方法配置聲明式事務(wù)管理,而卻要在這個service所調(diào)用的dao里得到當(dāng)前得session,這樣就會拋這個錯了:
          Java代碼 復(fù)制代碼
          1. if (method.getName().equals("getCurrentSession")) {   
          2.                 // Handle getCurrentSession method: return transactional Session, if any.   
          3.                 try {   
          4.                     return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);   
          5. //最后一個參數(shù)是false,說明這個方法不能返回一個新的session,沒有就拋異常   
          6.                 }   
          7.                 catch (IllegalStateException ex) {   
          8.                     throw new HibernateException(ex.getMessage());   
          9.                 }   
          10.             }  

          到這里事務(wù)開始部分基本就結(jié)束了
          按正常流程,那么接下來就是方法結(jié)束commit的問題了。

          posted on 2008-04-30 11:46 chu 閱讀(486) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(2)

          隨筆檔案

          我的鏈接

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 桐梓县| 长顺县| 东至县| 文山县| 诸暨市| 防城港市| 苏尼特左旗| 闻喜县| 涟水县| 阿城市| 博乐市| 孟村| 鄯善县| 师宗县| 东安县| 钦州市| 鄂伦春自治旗| 丹东市| 武冈市| 和林格尔县| 淄博市| 乌拉特前旗| 博白县| 天津市| 德保县| 依安县| 太康县| 南投市| 耒阳市| 盐池县| 晋州市| 睢宁县| 淄博市| 濮阳市| 凤凰县| 崇阳县| 济宁市| 东至县| 巢湖市| 柏乡县| 永川市|