1. 程式人生 > >spring框架基礎學習

spring框架基礎學習

一、背景

       在Spring事務:程式設計式事務管理這一篇中簡單的介紹了Spring中的程式設計式事務管理,本篇介紹下Spring中宣告式事務管理。

二、宣告式事務管理

1、Spring中宣告式事務管理概述

       Spring中的宣告式事務管理在底層是建立在AOP的基礎上的。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或加入一個事務,在執行完目標方法之後,根據執行情況再提交或回滾。

       宣告式事務最大的優點是不需要通過程式設計的方式管理事務,這樣就不需要在業務邏輯程式碼中加入事務管理程式碼,只需要在配置檔案中做相關的事務規則宣告,或者通過等價的基於標註的方式,便可以將事務應用到業務邏輯中。

       宣告式事務管理主要得益於Spring依賴注入容器和Spring AOP的支援。依賴注入容器為宣告式事務管理提供了基礎設施,是的bean對於Spring框架而言是可管理的;Spring AOP是宣告式事務管理的直接觀察者,這一點可以通過對應的配置檔案得到證實。

與程式設計式事務管理相比,宣告式唯一的不足是宣告式事務管理的粒度只能作用在方法級別,無法像程式設計式事務那樣可以作用到程式碼塊級別。但是,這點不足之處,有很多變通的方法,比如,可以將需要事務管理的程式碼塊單獨定義成一個方法,等等。

       從絕大多數資深的開發人員的經驗上看,他們更傾向於使用宣告式事務管理,這是因為宣告式事務管理不僅簡單,而且業務程式碼很純粹,不會收到汙染,方便後期的維護。

2、基於TransactionInterceptor的宣告式使館管理

Spring提供了TransactionInterceptor類來實施宣告式事務管理功能。配置檔案如下:

Xml程式碼  
  1. <span style="font-size: 16px;"><beans...>  
  2. ......  
  3.     <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  4.         <
    property name="transactionManager" ref="transactionManager"/>  
  5.         <property name="transactionAttributes">  
  6.             <props>  
  7.                <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  8.             </props>  
  9.         </property>  
  10.     </bean>  
  11.     <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl">  
  12.         <property name="bankDao" ref="bankDao"/>  
  13.     </bean>  
  14.     <bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean">  
  15.         <property name="target" ref="bankServiceTarget"/>  
  16.         <property name="interceptorNames">  
  17.            <list>  
  18.               <idref bean="transactionInterceptor"/>  
  19.            </list>  
  20.         </property>  
  21.     </bean>  
  22. ......  
  23. </beans></span>  

       首先,我們配置了一個TransactionInterceptor來定義相關的事務規則,它有兩個主要的屬性:一個是transactionManager,用來指定一個事務管理器,並將具體事務相關的操作委託給它;另一個是Properties型別的transactionAttributes屬性,它主要用來定義事務規則,該屬性的每一個鍵值對中,鍵指定的是方法名,方法名可以使用萬用字元,而值就表示相應方法的所應用的事務屬性。指定事務屬性的取值有較複雜的規則,這在Spring中算得上是一件讓人頭疼的事。具體的書寫規則如下:

描述性程式碼  
  1. 傳播行為 [,隔離級別] [,只讀屬性] [,超時屬性] [不影響提交的異常] [,導致回滾的異常]  

       傳播行為是唯一必須設定的屬性,其他都可以忽略,Spring為我們提供了合理的預設值。傳播行為的取值必須以“PROPAGATION_”開頭,共七種取值,如下表所示:            

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

         隔離級別的取值必須以“ISOLATION_”開頭,共五種取值,如下表所示:

ISOLATION_DEFAULT
ISOLATION_READ_COMMITTED
ISOLATION_READ_UNCOMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE

        如果事務是隻讀的,那麼我們可以指定只讀屬性,使用“readOnly”指定。否則我們不需要設定該屬性。超時屬性的取值必須以“TIMEOUT_”開頭,後面跟一個int型別的值,表示超時時間,單位是秒。不影響提交的異常是指,即使事務中丟擲了這些型別的異常,事務任然正常提交。必須在每一個異常的名字前面加上“+”。異常的名字可以是類名的一部分。比如,

“+RuntimeException”、“+tion”等等。導致回滾的異常是指,當事務中丟擲這些型別的異常時,事務將回滾。必須在每一個異常的名字前面加上“-”。異常的名字可以是類名的全部或者部分,比如“-RuntimeException”、“-tion”等等。下面給出兩個例項:

Xml程式碼  
  1. <span style="font-size: 16px;"><property name="*Service">  
  2.     PROPAGATION_REQUIRED,  
  3.     ISOLATION_READ_COMMITTED,  
  4.     TIMEOUT_20,  
  5.         +AbcException,  
  6.     +DefException,  
  7.     -HijException  
  8. </property></span>  
        以上表達式表示,針對所有方法名以Service結尾的方法,使用PROPAGATION_REQUIRED事務傳播行為,事務的隔離級別是ISOLATION_READ_COMMITTED,超時時間為20秒,當事務丟擲AbcException或者DefException型別的異常,則仍然提交,當丟擲HijException型別的異常時必須回滾事務。這裡沒有指定"readOnly",表示事務不是隻讀的。 Xml程式碼  
  1. <span style="font-size: 16px;"><property name="test">  
  2.     PROPAGATION_REQUIRED,  
  3.     readOnly  
  4. </property></span>  
       以上表達式表示,針對所有方法名為test的方法,使用PROPAGATION_REQUIRED事務傳播行為,並且該事務是隻讀的。除此之外,其他的屬性均使用預設值。比如,隔離級別和超時時間使用底層事務性資源的預設值,並且當發生未檢查異常,則回滾事務,發生已檢查異常則仍提交事務。

       配置好了TransactionInterceptor,還需要配置一個ProxyFactoryBean來組裝target和advice。這是典型的Spring AOP的做法。通過ProxyFactoryBean生成的代理類就是織入了事務管理邏輯後的目標類。至此,宣告式事務管理就算是實現了。我們沒有對業務程式碼進行任何操作,所有設定均在配置檔案中完成,這就是宣告式事務的最大優點。

3、基於TransactionProxy...的宣告式事務管理

       前面的宣告式事務雖然好,但是卻存在一個非常惱人的問題:配置檔案太多。必須針對每一個目標物件配置一個ProxyFactoryBean;另外,雖然可以通過父子Bean的方式來複用TransactionInterceptor的配置,但是實際的複用機率也不高;這樣,加上目標物件本身,每一個業務類可能需要對應三個 <bean/> 配置,隨著業務類的增多,配置檔案將會變得越來越龐大,管理配置檔案又成了問題。

       為了緩解這個問題,Spring提供了TransactionProxyFactoryBean,用於將TransactionInterceptor 和ProxyFactoryBean的配置合二為一。如下面的程式碼所示:

Xml程式碼  
  1. <span style="font-size: 16px;"><beans......>  
  2.     ......  
  3.     <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl">  
  4.        <property name="bankDao" ref="bankDao"/>  
  5.     </bean>  
  6.     <bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  7.        <property name="target" ref="bankServiceTarget"/>  
  8.        <property name="transactionManager" ref="transactionManager"/>  
  9.        <property name="transactionAttributes">  
  10.            <props>  
  11.              <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  12.            </props>  
  13.        </property>  
  14.     </bean>  
  15.     ......  
  16. </beans></span>  
       如此一來,配置檔案與先前相比簡化了很多,這種配置方式稱為Spring經典的宣告式事務管理。但是,顯式為每一個業務類配置一個TransactionProxyFactoryBean的做法將使得程式碼顯得過於刻板,為此可以使用自動建立代理的方式來將其簡化,使用自動建立代理是純 AOP 知識,請讀者參考相關文件。

4、基於<tx>名稱空間的宣告式事務管理

      前面兩種宣告式事務配置方式奠定了Spring宣告式事務管理的基石。在此基礎上,Spring 2.x 引入了<tx>名稱空間,結合使用<aop>名稱空間,帶給開發人員配置宣告式事務的全新體驗,配置變得更加簡單和靈活。另外,得益於 <aop> 名稱空間的切點表示式支援,宣告式事務也變得更加強大。如下面的配置檔案所示:

Xml程式碼  
  1. <span style="font-size: 16px;"><beans......>  
  2.     ......  
  3.     <bean id="bankService"  class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">  
  4.        <property name="bankDao" ref="bankDao"/>  
  5.     </bean>  
  6.     <tx:advice id="bankAdvice" transaction-manager="transactionManager">  
  7.        <tx:attributes>  
  8.           <tx:method name="transfer" propagation="REQUIRED"/>  
  9.        </tx:attributes>  
  10.     </tx:advice>  
  11.     <aop:config>  
  12.        <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>  
  13.        <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>  
  14.     </aop:config>  
  15.     ......  
  16. </beans></span>