程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Spring源代碼解析(六):Spring聲明式事務處理

Spring源代碼解析(六):Spring聲明式事務處理

編輯:關於JAVA

我們看看Spring中的事務處理的代碼,使用Spring管理事務有聲明式和編程式兩種方 式,聲明式事務處理通過AOP的實現把事物管理代碼作為方面封裝來橫向插入到業務代碼 中,使得事務管理代碼和業務代碼解藕。在這種方式我們結合IoC容器和Spirng已有的 FactoryBean來對事務管理進行屬性配置,比如傳播行為,隔離級別等。其中最簡單的方 式就是通過配置TransactionProxyFactoryBean來實現聲明式事物;

在整個源代碼分析中,我們可以大致可以看到Spring實現聲明式事物管理有這麼幾個 部分:

* 對在上下文中配置的屬性的處理,這裡涉及的類是 TransactionAttributeSourceAdvisor,這是一個通知器,用它來對屬性值進行處理,屬 性信息放在TransactionAttribute中來使用,而這些屬性的處理往往是和對切入點的處理 是結合起來的。對屬性的處理放在類TransactionAttributeSource中完成。

* 創建事物的過程,這個過程是委托給具體的事物管理器來創建的,但Spring通過 TransactionStatus來傳遞相關的信息。

* 對事物的處理通過對相關信息的判斷來委托給具體的事物管理器完成。

我們下面看看具體的實現,在TransactionFactoryBean中:

Java代碼

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
     implements FactoryBean, BeanFactoryAware {
//這裡是Spring事務處理而使用的AOP攔截器,中間封裝了Spring對事務處理的代碼來 支持聲明式事務處理的實現
   private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
   private Pointcut pointcut;
//這裡Spring把TransactionManager注入到TransactionInterceptor中去
   public void setTransactionManager(PlatformTransactionManager transactionManager) {
     this.transactionInterceptor.setTransactionManager (transactionManager);
   }
//這裡把在bean配置文件中讀到的事務管理的屬性信息注入到TransactionInterceptor 中去
   public void setTransactionAttributes(Properties transactionAttributes) {
     this.transactionInterceptor.setTransactionAttributes (transactionAttributes);
   }
   .........中間省略了其他一些方法.......
   //這裡創建Spring AOP對事務處理的Advisor
   protected Object createMainInterceptor() {
     this.transactionInterceptor.afterPropertiesSet();
     if (this.pointcut != null) {
       //這裡使用默認的通知器
       return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
     }
     else {
       // 使用上面定義好的TransactionInterceptor作為攔截器,同時使用 TransactionAttributeSourceAdvisor
       return new TransactionAttributeSourceAdvisor (this.transactionInterceptor);
     }
   }
}

那什麼時候Spring的TransactionInterceptor被注入到Spring AOP中成為Advisor中的 一部分呢?我們看到在TransactionProxyFactoryBean中,這個方法在IOC初始化bean的時 候被執行:

Java代碼

public void afterPropertiesSet() {
     .......
     //TransactionProxyFactoryBean實際上使用ProxyFactory完成AOP的基本功 能。
     ProxyFactory proxyFactory = new ProxyFactory();
     if (this.preInterceptors != null) {
       for (int i = 0; i < this.preInterceptors.length; i++) {
         proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap (this.preInterceptors[i]));
       }
     }
     //這裡是Spring加入通知器的地方
     //有兩種通知器可以被加入DefaultPointcutAdvisor或者 TransactionAttributeSourceAdvisor
     //這裡把Spring處理聲明式事務處理的AOP代碼都放到ProxyFactory中去,怎 樣加入advisor我們可以參考ProxyFactory的父類AdvisedSupport()
     //由它來維護一個advice的鏈表,通過這個鏈表的增刪改來抽象我們對整個 通知器配置的增刪改操作。
     proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap (createMainInterceptor()));
     if (this.postInterceptors != null) {
       for (int i = 0; i < this.postInterceptors.length; i++) {
         proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap (this.postInterceptors[i]));
       }
     }
     proxyFactory.copyFrom(this);

     //這裡創建AOP的目標源
     TargetSource targetSource = createTargetSource(this.target);
     proxyFactory.setTargetSource(targetSource);
     if (this.proxyInterfaces != null) {
       proxyFactory.setInterfaces(this.proxyInterfaces);
     }
     else if (!isProxyTargetClass()) {
       proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass (targetSource.getTargetClass()));
     }
     this.proxy = getProxy(proxyFactory);
   }

Spring 已經定義了一個transctionInterceptor作為攔截器或者AOP advice的實現, 在IOC容器中定義的其他屬性比如transactionManager和事務管理的屬性都會傳到已經定 義好的 TransactionInterceptor那裡去進行處理。以上反映了基本的Spring AOP的定義 過程,其中pointcut和advice都已經定義好,同時也通過通知器配置到ProxyFactory中去 了。

下面讓我們回到TransactionProxyFactoryBean中看看 TransactionAttributeSourceAdvisor是怎樣定義的,這樣我們可以理解具體的屬性是怎 樣起作用,這裡我們分析一下類TransactionAttributeSourceAdvisor:

Java代碼

public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
   //和其他Advisor一樣,同樣需要定義AOP中的用到的Interceptor和Pointcut
   //Interceptor使用傳進來的TransactionInterceptor
   //而對於pointcut,這裡定義了一個內部類,參見下面的代碼 
   private TransactionInterceptor transactionInterceptor;
   private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();

   .........
   //定義的PointCut內部類
     private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    .......
    //方法匹配的實現,使用了TransactionAttributeSource類
     public boolean matches(Method method, Class targetClass) {
       TransactionAttributeSource tas = getTransactionAttributeSource ();
       //這裡使用TransactionAttributeSource來對配置屬性進行處理
       return (tas != null && tas.getTransactionAttribute (method, targetClass) != null);
     }
   ........省略了equal,hashcode,tostring的代碼
   }

這裡我們看看屬性值是怎樣被讀入的: AbstractFallbackTransactionAttributeSource負責具體的屬性讀入任務,我們可以有兩 種讀入方式,比如annotation和直接配置.我們下面看看直接配置的讀入方式,在Spring 中同時對讀入的屬性值進行了緩存處理,這是一個decorator模式:

Java代碼

public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
     //這裡先查一下緩存裡有沒有事務管理的屬性配置,如果有從緩存中取得 TransactionAttribute
     Object cacheKey = getCacheKey(method, targetClass);
     Object cached = this.cache.get(cacheKey);
     if (cached != null) {
       if (cached == NULL_TRANSACTION_ATTRIBUTE) {
         return null;
       }
       else {
         return (TransactionAttribute) cached;
       }
     }
     else {
       // 這裡通過對方法和目標對象的信息來計算事務緩存屬性
       TransactionAttribute txAtt = computeTransactionAttribute (method, targetClass);
       //把得到的事務緩存屬性存到緩存中,下次可以直接從緩存中取得。
       if (txAtt == null) {
         this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
       }
       else {
         ...........
         this.cache.put(cacheKey, txAtt);
       }
       return txAtt;
     }
   }

別急,基本的處理在computeTransactionAttribute()中:

Java代碼

private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {
     //這裡檢測是不是public方法
     if(allowPublicMethodsOnly() && !Modifier.isPublic (method.getModifiers())) {
       return null;
     }

     Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

     // First try is the method in the target class.
     TransactionAttribute txAtt = findTransactionAttribute (findAllAttributes(specificMethod));
     if (txAtt != null) {
       return txAtt;
     }
     // Second try is the transaction attribute on the target class.
     txAtt = findTransactionAttribute(findAllAttributes (specificMethod.getDeclaringClass()));
     if (txAtt != null) {
       return txAtt;
     }
     if (specificMethod != method) {
       // Fallback is to look at the original method.
       txAtt = findTransactionAttribute(findAllAttributes (method));
       if (txAtt != null) {
         return txAtt;
       }
       // Last fallback is the class of the original method.
       return findTransactionAttribute(findAllAttributes (method.getDeclaringClass()));
     }
     return null;
   }

經過一系列的嘗試我們可以通過findTransactionAttribute()通過調用 findAllAttribute()得到TransactionAttribute的對象,如果返回的是null,這說明該方 法不是我們需要事務處理的方法。

在完成把需要的通知器加到ProxyFactory中去的基礎上,我們看看具體的看事務處理 代碼怎樣起作用,在TransactionInterceptor中:

Java代碼

public Object invoke(final MethodInvocation invocation) throws Throwable {
     //這裡得到目標對象
     Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);
     //這裡同樣的通過判斷是否能夠得到TransactionAttribute來決定是否對當 前方法進行事務處理,有可能該屬性已經被緩存,
     //具體可以參考上面對getTransactionAttribute的分析,同樣是通過 TransactionAttributeSource
     final TransactionAttribute txAttr =
         getTransactionAttributeSource().getTransactionAttribute (invocation.getMethod(), targetClass);
     final String joinpointIdentification = methodIdentification (invocation.getMethod());
     //這裡判斷我們使用了什麼TransactionManager
     if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
       // 這裡創建事務,同時把創建事務過程中得到的信息放到 TransactionInfo中去
       TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
       Object retVal = null;
       try {
          retVal = invocation.proceed();
       }
       catch (Throwable ex) {
         // target invocation exception
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
       }
       finally {
         cleanupTransactionInfo(txInfo);
       }
       commitTransactionAfterReturning(txInfo);
       return retVal;
     }
     else {
       // 使用的是Spring定義的PlatformTransactionManager同時實現了回調 接口,我們通過其回調函數完成事務處理,就像我們使用編程式事務處理一樣。
       try {
         Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager ()).execute(txAttr,
             new TransactionCallback() {
               public Object doInTransaction(TransactionStatus status) {
                 //同樣的需要一個TransactonInfo
                 TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
                 try {
                   return invocation.proceed();
                 }
               .....這裡省去了異常處理和事務信息的清理代碼
             });
       ...........
     }
   }

這裡面涉及到事務的創建,我們可以在TransactionAspectSupport實現的事務管理代 碼:

Java代碼

protected TransactionInfo createTransactionIfNecessary(
       TransactionAttribute txAttr, final String joinpointIdentification) {
     // If no name specified, apply method identification as transaction name.
     if (txAttr != null && txAttr.getName() == null) {
       txAttr = new DelegatingTransactionAttribute(txAttr) {
         public String getName() {
           return joinpointIdentification;
         }
       };
     }
     TransactionStatus status = null;
     if (txAttr != null) {
     //這裡使用了我們定義好的事務配置信息,有事務管理器來創建事務,同時返 回TransactionInfo
       status = getTransactionManager().getTransaction(txAttr);
     }
     return prepareTransactionInfo(txAttr, joinpointIdentification, status);
   }

首先通過TransactionManager得到需要的事務,事務的創建根據我們定義的事務配置 決定,在 AbstractTransactionManager中給出一個標准的創建過程,當然創建什麼樣的 事務還是需要具體的 PlatformTransactionManager來決定,但這裡給出了創建事務的模 板:

Java代碼

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
     Object transaction = doGetTransaction();
     ......
     if (definition == null) {
       //如果事務信息沒有被配置,我們使用Spring默認的配置方式
       definition = new DefaultTransactionDefinition();
     }
     if (isExistingTransaction(transaction)) {
       // Existing transaction found -> check propagation behavior to find out how to behave.
       return handleExistingTransaction(definition, transaction, debugEnabled);
     }
     // Check definition settings for new transaction.
     //下面就是使用配置信息來創建我們需要的事務;比如傳播屬性和同步屬性等
     //最後把創建過程中的信息收集起來放到TransactionStatus中返回; 
     if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
       throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
     }
     // No existing transaction found -> check propagation behavior to find out how to behave.
     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
       throw new IllegalTransactionStateException(
           "Transaction propagation 'mandatory' but no existing transaction found");
     }
     else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
       definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
       //這裡是事務管理器創建事務的地方,並將創建過程中得到的信息放到 TransactionStatus中去,包括創建出來的事務
       doBegin(transaction, definition);
       boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
       return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
     }
     else {
       boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
       return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);
     }
   }

接著通過調用prepareTransactionInfo完成事務創建的准備,創建過程中得到的信息 存儲在TransactionInfo對象中進行傳遞同時把信息和當前線程綁定;

Java代碼

protected TransactionInfo prepareTransactionInfo(
       TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
     TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);
     if (txAttr != null) {
     .....
       // 同樣的需要把在getTransaction中得到的TransactionStatus放到 TransactionInfo中來。
       txInfo.newTransactionStatus(status);
     }
     else {
     .......
    }
     // 綁定事務創建信息到當前線程
     txInfo.bindToThread();
     return txInfo;
   }

將創建事務的信息返回,然後看到其他的事務管理代碼:

Java代碼

protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
     if (txInfo != null && txInfo.hasTransaction()) {
       if (logger.isDebugEnabled()) {
         logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());
       }
       this.transactionManager.commit(txInfo.getTransactionStatus ());
     }
   }

通過transactionManager對事務進行處理,包括異常拋出和正常的提交事務,具體的 事務管理器由用戶程序設定。

Java代碼

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
     if (txInfo != null && txInfo.hasTransaction()) {
       if (txInfo.transactionAttribute.rollbackOn(ex)) {
         ......
         try {
           this.transactionManager.rollback (txInfo.getTransactionStatus());
         }
         ..........
    }
       else {
         .........
         try {
           this.transactionManager.commit (txInfo.getTransactionStatus());
         }
    ...........
   }
   protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
     if (txInfo != null && txInfo.hasTransaction()) {
       ......
       this.transactionManager.commit(txInfo.getTransactionStatus ());
     }
   }

Spring通過以上代碼對transactionManager進行事務處理的過程進行了AOP包裝,到這 裡我們看到為了方便客戶實現聲明式的事務處理,Spring還是做了許多工作的。如果說使 用編程式事務處理,過程其實比較清楚,我們可以參考書中的例子:

Java代碼

TransactionDefinition td = new DefaultTransactionDefinition();
   TransactionStatus status = transactionManager.getTransaction(td);
   try{
      ......//這裡是我們的業務方法
   }catch (ApplicationException e) {
    transactionManager.rollback(status);
    throw e
   }
   transactionManager.commit(status);
   ........

我們看到這裡選取了默認的事務配置DefaultTransactionDefinition,同時在創建事 物的過程中得到TransactionStatus,然後通過直接調用事務管理器的相關方法就能完成事 務處理。

聲明式事務處理也同樣實現了類似的過程,只是因為采用了聲明的方法,需要增加對 屬性的讀取處理,並且需要把整個過程整合到Spring AOP框架中和IoC容器中去的過程。

下面我們選取一個具體的transactionManager - DataSourceTransactionManager來看 看其中事務處理的實現:

同樣的通過使用AbstractPlatformTransactionManager使用模板方法,這些都體現了 對具體平台相關的事務管理器操作的封裝,比如commit:

Java代碼

public final void commit(TransactionStatus status) throws TransactionException {
     ......
     DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
     if (defStatus.isLocalRollbackOnly()) {
       ......
       processRollback(defStatus);
       return;
     }
       .......
       processRollback(defStatus);
     ......
     }
     processCommit(defStatus);
   }

通過對TransactionStatus的具體狀態的判斷,來決定具體的事務處理:

Java代碼

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
     try {
       boolean beforeCompletionInvoked = false;
       try {
         triggerBeforeCommit(status);
         triggerBeforeCompletion(status);
         beforeCompletionInvoked = true;
         boolean globalRollbackOnly = false;
         if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
           globalRollbackOnly = status.isGlobalRollbackOnly();
         }
         if (status.hasSavepoint()) {
         ........
          status.releaseHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
         ......
           doCommit(status);
         }
       .........
   }

這些模板方法的實現由具體的transactionManager來實現,比如在 DataSourceTransactionManager:

Java代碼

protected void doCommit(DefaultTransactionStatus status) {
     //這裡得到存在TransactionInfo中已經創建好的事務
     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
     //這裡得到和事務綁定的數據庫連接
     Connection con = txObject.getConnectionHolder().getConnection();
     ........
     try {
     //這裡通過數據庫連接來提交事務
       con.commit();
     }
    .......
   }
   protected void doRollback(DefaultTransactionStatus status) {
     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
     Connection con = txObject.getConnectionHolder().getConnection();
     if (status.isDebug()) {
       logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
     }
     try {
     //這裡通過數據庫連接來回滾事務
       con.rollback();
     }
     catch (SQLException ex) {
       throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
     }
   }

我們看到在DataSourceTransactionManager中最後還是交給connection來實現事務的 提交和rollback。整個聲明式事務處理是事務處理在Spring AOP中的應用,我們看到了一 個很好的使用Spring AOP的例子,在Spring聲明式事務處理的源代碼中我們可以看到:

1.怎樣封裝各種不同平台下的事務處理代碼

2.怎樣讀取屬性值和結合事務處理代碼來完成既定的事務處理策略

3.怎樣靈活的使用SpringAOP框架。

如果能夠結合前面的Spring AOP的源代碼來學習,理解可能會更深刻些。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved