posts - 495,  comments - 11,  trackbacks - 0

          6.3.2 Spring事務策略的優(yōu)勢

          雖然在上面的配置片段中,僅僅配置了JDBC局部事務管理器、Hibernate局部事務管理器、JDBC全局事務管理器等。但Spring支持大部分持久化策略的事務管理器。

          不論采用何種持久化策略,Spring都提供了一致的事務抽象,因此,應用開發(fā)者能在任何環(huán)境下,使用一致的編程模型。無須更改代碼,應用就可在不同的事務管理策略中切換。Spring同時支持聲明式事務管理和編程式事務管理。

          使用編程式事務管理,開發(fā)者使用的是Spring事務抽象,而無須使用任何具體的底層事務API。Spring的事務管理將代碼從底層具體的事務API中抽象出來,該抽象可以使用在任何底層事務基礎之上。

          使用聲明式策略,開發(fā)者通常書寫很少的事務管理代碼,因此,不依賴Spring或任何其他事務API。Spring的聲明式事務無須任何額外的容器支持,Spring容器本身管理聲明式事務。使用聲明事務策略,無須在業(yè)務代碼中書寫任何事務代碼,可以讓開發(fā)者更好地專注于業(yè)務邏輯的實現(xiàn)。Spring管理的事務支持多個事務資源的跨越,但無法支持跨越遠程調用的事務上下文傳播。

          6.3.3 使用TransactionProxyFactoryBean創(chuàng)建事務代理

          Spring同時支持編程式事務策略和聲明式事務策略,大部分時候,都推薦采用聲明式事務策略,使用聲明事務策略的優(yōu)勢十分明顯:

          ?? ● 聲明式事務能大大降低開發(fā)者的代碼書寫量。而且聲明式事務幾乎不需要影響應用的代碼。因此,無論底層事務策略如何變化,應用程序無須任何改變。

          ?? ● 應用程序代碼無須任何事務處理代碼,可以更專注于業(yè)務邏輯的實現(xiàn)。

          ?? ● Spring則可對任何POJO的方法提供事務管理,而且Spring的聲明式事務管理無須容器的支持,可在任何環(huán)境下使用。

          ?? ● EJB的CMT無法提供聲明式回滾規(guī)則。而通過配置文件,Spring可指定事務在遇到特定異常時自動回滾。Spring不僅可在代碼中使用setRollbackOnly回滾事務,也可在配置文件中配置回滾規(guī)則。

          ?? ● 由于Spring采用AOP的方式管理事務,因此,可以在事務回滾動作中插入用戶自己的動作,而不僅僅是執(zhí)行系統(tǒng)默認的回滾。

          提示:本節(jié)不打算全面介紹Spring的各種事務策略,因此本節(jié)不會介紹編程式事務。如果讀者需要更全面了解Spring事務的相關方面,請參閱筆者所著的《Spring2.0寶典》???? 一書。

          對于采用聲明式事務策略,可以使用TransactionProxyFactoryBean來配置事務代理Bean。正如它的類名所暗示的,它是一個工廠Bean,工廠Bean用于生成一系列的Bean實例,這一系列的Bean實例都是Proxy。

          可能讀者已經(jīng)想到了,既然TransactionProxyFactoryBean產(chǎn)生的是代理Bean,可見這種事務代理正是基于Spring AOP組件的。配置TransactionProxyFactoryBean時,一樣需要指定目標Bean。

          每個TransactionProxyFactoryBean為一個目標Bean生成事務代理,事務代理的方法改寫了目標Bean的方法,就是在目標Bean的方法執(zhí)行之前加入開始事務,在目標Bean的方法正常結束之前提交事務,如果遇到特定異常則回滾事務。

          TransactionProxyFactoryBean創(chuàng)建事務代理時,需要了解當前事務所處的環(huán)境,該環(huán)境屬性通過PlatformTransactionManager實例傳入,而相關事務傳入規(guī)則在TransactionProxy- FactoryBean的定義中給出。

          下面給出聲明式事務配置文件的完整代碼:

          <?xml version="1.0" encoding="GBK"?>

          <!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

          <beans xmlns="http://www.springframework.org/schema/beans"

          ?????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ?????? xsi:schemaLocation="http://www.springframework.org/schema/beans

          http://www.springframework.org/schema/beans/spring-beans.xsd">

          ??? <!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) -->

          ??? <bean id="dataSource" class="com.mchange.v2.c3p0.
          ??? ComboPooledDataSource" destroy-method="close">

          ??????? <!-- 指定連接數(shù)據(jù)庫的驅動 -->

          ??? ??? <property name="driverClass" value="com.mysql.jdbc.Driver"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的URL -->

          ??? ??? <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的用戶名 -->

          ??? ??? <property name="user" value="root"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的密碼 -->

          ??? ??? <property name="password" value="32147"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) -->

          ??? ??? <property name="maxPoolSize" value="40"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) -->

          ??? ??? <property name="minPoolSize" value="1"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) -->

          ??? ??? <property name="initialPoolSize" value="1"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的連接最大空閑時間 -->

          ??? ??? <property name="maxIdleTime" value="20"/>

          ??? </bean>

          ??? <!-- 定義Hibernate的SessionFactory -->

          ??? <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
          ??? LocalSessionFactoryBean">

          ??????? <!-- 依賴注入SessionFactory所需的數(shù)據(jù)源,正是上文定義的dataSource -->

          ??????? <property name="dataSource" <ref="dataSource"/>

          ??????? <!-- mappingResources屬性用來列出全部映射文件 -->

          ??????? <property name="mappingResources">

          ??????????? <list>

          ??????????????? <!-- 以下用來列出所有的PO映射文件 -->

          ??????????????? <value>lee/Person.hbm.xml</value>

          ??????????? </list>

          ??????? </property>

          ????????? <!-- 定義Hibernate的SessionFactory屬性 -->

          ??????? <property name="hibernateProperties">

          ??????? ???? <props>

          ??????????????? <!-- 指定Hibernate的連接方言 -->

          ??????????? ??? <prop key="hibernate.dialect">org.hibernate.dialect.
          ??????????????? MySQLDialect</prop>

          ??????????????? <!-- 是否根據(jù)Hiberante映射創(chuàng)建數(shù)據(jù)表時,選擇create、update、
          ??????????????? create-drop -->

          ??????????? ????? <prop key="hibernate.hbm2ddl.auto">update</prop>

          ??????? ???? </props>

          ??????? </property>

          ??? </bean>

          ??? <!-- 配置DAO Bean,該Bean將作為目標Bean使用 -->

          ??? <bean id="personDAOTarget" class="lee.PersonDaoImpl">

          ??????? <!-- 采用依賴注入來傳入SessionFactory的引用 -->

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

          ??? </bean>

          ??? <!-- 配置Hibernate的事務管理器 -->

          ??? <!-- 使用HibernateTransactionManager類,該類實現(xiàn)PlatformTransactionManager
          ??? 接口,針對采用Hibernate持久化連接的特定實現(xiàn) -->

          ??? <bean id="transactionManager"

          ??????? class="org.springframework.orm.hibernate3.
          ??????? HibernateTransactionManager">

          ??????? <!-- HibernateTransactionManager Bean,它需要依賴注入一個SessionFactory
          ??????? Bean的引用 -->

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

          ??? </bean>

          ??? <!-- 配置personDAOTarget Bean的事務代理 -->

          ??? <bean id="personDAO"

          ??????? class="org.springframework.transaction.interceptor.
          ??????? TransactionProxyFactoryBean">

          ??????? <!-- 依賴注入PlatformTransactionManager的bean引用,此處使用
          ??????? Hibernate的bean -->

          ??????? <!-- 局部事務器,因此transactionManager 傳入Hibernate事務管理器的
          ??????? 引用 -->

          ??? ??? ?? <property name="transactionManager" ref="transactionManager"/>

          ??????? <!-- 需要生成代理的目標bean -->

          ??? ??? ?? <property name="target" ref="personDAOTarget"/>

          ??????? <!-- 指定事務屬性 -->

          ??? ??? ?? <property name="transactionAttributes">

          ??????? ??? ?? <props>

          ??????????????? <!-- 以下部分為定義事務回滾規(guī)則 -->

          ??????????? ??? ?? <prop key="insert*">PROPAGATION_REQUIRED,
          ??????????????? -MyCheckedException</prop>

          ??????????? ??? ?? <prop key="update*">PROPAGATION_REQUIRED</prop>

          ??????????? ??? ?? <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

          ??????? ??? </props>

          ??? ??? </property>

          ??? </bean>

          </beans>

          在上面的定義文件中,沒有對DAO對象采用Service層包裝。通常情況下,DAO層上應有一層Service層。事務代理則以Service層Bean為目標Bean。此處為了簡化配置,TransactionProxyFactoryBean直接以DAO bean作為目標bean,這一點不會影響事務代理的生成。

          事務回滾規(guī)則部分定義了三個回滾規(guī)則:

          第一個回滾規(guī)則表示所有以insert開始的方法,都應該滿足該事務規(guī)則。PROPAGATION_REQUIRED事務傳播規(guī)則指明,該方法必須處于事務環(huán)境中,如果當前執(zhí)行線程已處于事務環(huán)境下,則直接執(zhí)行;否則,啟動新的事務然后執(zhí)行該方法。該規(guī)則還指定,如果方法拋出MyCheckedException的實例及其子類的實例,則強制回滾。MyCheckedException前的“-”表示強制回滾;“+”則表示強制提交,即某些情況下,即使拋出異常也強制提交;

          第二個回滾規(guī)則表示所有以update開頭的方法,都遵守PROPAGATION_REQUIRED的事務傳播規(guī)則;

          第三個回滾規(guī)則表示除前面規(guī)定的方法外,其他所有方法都采用PROPAGATION_ REQUIRED事務傳播規(guī)則,而且只讀。

          常見的事務傳播規(guī)則有如下幾個:

          ?? ● PROPAGATION_MANDATORY,要求調用該方法的線程必須處于事務環(huán)境中,否則拋出異常。

          ?? ● PROPAGATION_NESTED,如果執(zhí)行該方法的線程已處于事務環(huán)境下,依然啟動新的事務,方法在嵌套的事務里執(zhí)行。如果執(zhí)行該方法的線程序并未處于事務中,也啟動新的事務,然后執(zhí)行該方法,此時與PROPAGATION_REQUIRED相同。

          ?? ● PROPAGATION_NEVER,不允許調用該方法的線程處于事務環(huán)境下,如果調用該方法的線程處于事務環(huán)境下,則拋出異常。

          ?? ● PROPAGATION_NOT_SUPPORTED,如果調用該方法的線程處在事務中,則先暫停當前事務,然后執(zhí)行該方法。

          ?? ● PROPAGATION_REQUIRED,要求在事務環(huán)境中執(zhí)行該方法,如果當前執(zhí)行線程已處于事務中,則直接調用;如果當前執(zhí)行線程不處于事務中,則啟動新的事務后執(zhí)行該方法。

          ?? ● PROPAGATION_REQUIRES_NEW,該方法要求有一個線程在新的事務環(huán)境中執(zhí)行,如果當前執(zhí)行線程已處于事務中,先暫停當前事務,啟動新的事務后執(zhí)行該方法;如果當前調用線程不處于事務中,則啟動新的事務后執(zhí)行該方法。

          ?? ● PROPAGATION_SUPPORTS,如果當前執(zhí)行線程處于事務中,則使用當前事務,否則不使用事務。

          程序里原來使用personDAO的地方,無須變化。因為,配置文件里將personDAO目標Bean的id改成personDAOTarget,為TransactionProxyFactoryBean工廠Bean所產(chǎn)生的代理Bean命名為personDAO。該代理Bean會包含原有personDAO的所有方法,而且為這些方法增加了不同的事務處理規(guī)則。

          程序面向PersonDaoImpl類所實現(xiàn)的接口編程,TransactionProxyFactoryBean生成的代理Bean也會實現(xiàn)TransactionProxyFactoryBean接口。因此,原有的程序中調用DAO組件的代碼無須任何改變。程序運行時,由事務代理完成原來目標Bean完成的工作。

          事實上,Spring不僅支持對接口的代理,整合CGLIB后,Spring甚至可對具體類生成代理。只要設置proxyTargetClass屬性為true就可以。如果目標Bean沒有實現(xiàn)任何接口,proxyTargetClass屬性默認被設為true,此時Spring會對具體類生成代理。當然,通常建議面向接口編程,而不要面向具體的實現(xiàn)類編程。

          6.3.4 使用繼承簡化事務配置

          仔細觀察配置文件中兩個事務代理Bean的配置時,發(fā)現(xiàn)兩個事務代理Bean的大部分配置完全相同,如果配置文件中包含大量這樣的事務代理Bean配置,配置文件將非常臃腫。考慮到大部分事務代理Bean的配置都大同小異,可以使用Bean繼承來簡化事務代理的配置。

          正如前面部分介紹的,Bean繼承是將所有子Bean中相同的配置定義成一個模板,并將此模板Bean定義成一個抽象Bean??紤]所有事務代理Bean中,有如下部分是大致相?? 同的:

          ?? ● 事務代理Bean所使用的事務管理器。

          ?? ● 事務傳播規(guī)則。

          因此,現(xiàn)在配置文件中定義如下的事務代理模板Bean,其配置代碼如下:

          <!-- 定義所有事務代理Bean的模板 -->

          <bean id="txProxyTemplate" abstract="true"

          ??????? class="org.springframework.transaction.interceptor.
          ??????? TransactionProxyFactoryBean">

          ??? <!-- 為事務代理Bean注入生成代理所需的PlatformTransactionManager實例 -->

          ??? <property name="transactionManager" ref="transactionManager"/>

          ??????? <!-- 定義生成事務代理通用的事務屬性 -->

          ??????? <property name="transactionAttributes">

          ??????????? <props>

          ??????????????? <!-- 所有的方法都應用PROPAGATION_REQUIRED的事務傳播規(guī)則 -->

          ??????????????? <prop key="*">PROPAGATION_REQUIRED</prop>

          ??????????? </props>

          ??? </property>

          </bean>

          而真正的事務代理Bean,則改為繼承上面的事務模板Bean??紤]到將目標Bean定義在Spring容器中可能增加未知的風險,因此將目標Bean定義成嵌套Bean。

          <!-- 讓事務代理Bean繼承模板Bean -->

          <bean id="personDAO" parent="txProxyTemplate">

          ??? <!-- 這里采用嵌套Bean的方式來定義目標Bean,當然也可以引用已存在的Bean -->

          ??? <property name="target">

          ??????? <bean class="lee.personDAO"/>

          ??? </property>

          </bean>

          此時的personDAO Bean無須具體地定義事務屬性,它將在其父Bean txProxyTemplate中獲取事務定義屬性。此處采用嵌套Bean來定義目標Bean,因此,并未將目標Bean直接暴露在Spring的上下文中讓其他模塊調用。當然,也可采用一個已經(jīng)存在的Bean作為目標Bean;子Bean的事務屬性定義,完全可覆蓋事務代理模板里的事務屬性定義。如下例所示:

          <!-- 讓事務代理bean繼承模板Bean -->

          <bean id="personDAO" parent="txProxyTemplate">

          ??? <!-- 這里,采用引用已存在的bean的方式來定義目標Bean -->

          ??? <property name="target" ref ="personDAOTarget"/>

          ??? <!-- 覆蓋事務代理模板bean中的事務屬性定義 -->

          ??? <property name="transactionAttributes">

          ??????? <props>

          ??????????? <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>

          ??????????? <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

          ??????????? <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>

          ??? ???? </props>

          </property>

          </bean>

          可見,采用Bean繼承方式定義事務代理的方式,可以很好地簡化事務代理的配置,可以避免配置事務代理Bean時的冗余配置。

          提示:使用Bean繼承可以很好地簡化事務代理Bean的配置,通過將各事務代理Bean共同的配置信息提取成事務模板Bean,可以讓實際的事務代理Bean的配置更加簡潔;而且,配置方式相當直觀。盡量將目標Bean配置成嵌套Bean,這樣的方式可以保證更好的內聚性。

          如果讀者還記得前面介紹的AOP知識,應該知道還有一種更加簡潔的配置,就是利用Bean后處理器,讓Bean后處理器為容器中其他Bean自動創(chuàng)建事務代理。

          6.3.5 使用自動創(chuàng)建代理簡化事務配置

          回顧6.2.6節(jié)和6.2.7節(jié),讀者可能已經(jīng)想到如何自動創(chuàng)建代理。是的,正是通過6.2.6節(jié)和6.2.7節(jié)所給出的兩個自動代理創(chuàng)建類來生成事務代理。

          正如前文已經(jīng)提到的,使用BeanNameAutoProxyCreator和DefaultAdvisorAutoProxy- Creator來創(chuàng)建代理時,并不一定是創(chuàng)建事務代理,關鍵在于傳入的攔截器,如果傳入事務攔截器,將可自動生成事務代理。

          下面是使用BeanNameAutoProxyCreator自動生成事務代理的配置文件:

          <?xml version="1.0" encoding="GBK"?>

          <!-- 指定Spring配置文件的根元素,以及相應的Schema信息 -->

          <beans xmlns="http://www.springframework.org/schema/beans"

          ?????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ?????? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

          ??? <!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) -->

          ??? <bean id="dataSource" class="com.mchange.v2.c3p0.
          ??? ComboPooledDataSource" destroy-method="close">

          ??????? <!-- 指定連接數(shù)據(jù)庫的驅動 -->

          ??????? <property name="driverClass" value="com.mysql.jdbc.Driver"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的URL -->

          ??????? <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的用戶名 -->

          ??????? <property name="user" value="root"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫的密碼 -->

          ??????? <property name="password" value="32147"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) -->

          ??????? <property name="maxPoolSize" value="40"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) -->

          ??????? <property name="minPoolSize" value="1"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) -->

          ??????? <property name="initialPoolSize" value="1"/>

          ??????? <!-- 指定連接數(shù)據(jù)庫連接池的連接最大空閑時間 -->

          ??????? <property name="maxIdleTime" value="20"/>

          ??? </bean>

          ??? <!-- 使用JDBC的局部事務策略 -->

          ??? <bean id="transactionManager"

          ??????? class="org.springframework.jdbc.datasource.DataSource-
          ??????? TransactionManager">

          ??????? <!-- 為事務管理器注入所需的數(shù)據(jù)源Bean -->

          ??????? <property name="dataSource" ref="dataSource"/>

          ??? </bean>

          ??? <!-- 配置目標Bean,該目標Bean將由Bean后處理器自動生成代理 -->

          ??? <bean id="test1" class="lee.TransactionTestImpl">

          ??????? <!-- 依賴注入目標Bean所必需的數(shù)據(jù)源Bean -->

          ??? ??? <property name="ds" ref="dataSource"/>

          ??? </bean>

          ??? <!-- 配置目標Bean,該目標Bean將由Bean后處理器自動生成代理 -->

          ??? <bean id="test2" class="lee.TestImpl">

          ??????? <!-- 依賴注入目標Bean所必需的數(shù)據(jù)源Bean -->

          ??? ??? <property name="ds" ref="dataSource"/>

          ??? </bean>

          ??? <!-- 配置事務攔截器Bean -->

          ??? <bean id="transactionInterceptor"

          ??????? class="org.springframework.transaction.interceptor.
          ??????? TransactionInterceptor">

          ??????? <!-- 事務攔截器bean需要依賴注入一個事務管理器 -->

          ??????? <property name="transactionManager" ref="transactionManager"/>

          ??????? <property name="transactionAttributes">

          ??????????? <!-- 下面定義事務傳播屬性 -->

          ??????????? <props>

          ??????????????? <prop key="insert*">PROPAGATION_REQUIRED</prop>

          ??????????????? <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

          ??????????????? <prop key="*">PROPAGATION_REQUIRED</prop>

          ??????????? </props>

          ??????? </property>

          ??? </bean>

          ??? <!-- 定義BeanNameAutoProxyCreator的Bean后處理器 -->

          ??? <bean class="org.springframework.aop.framework.autoproxy.
          ??? BeanNameAutoProxyCreator">

          ??? <!-- 指定對滿足哪些bean name的bean自動生成業(yè)務代理 -->

          ??? ??? <property name="beanNames">

          ??????????? <!-- 下面是所有需要自動創(chuàng)建事務代理的Bean -->

          ??????????? <list>

          ??????????????? <value>test1</value>

          ??????????????? <value>test2</value>

          ??????????? </list>

          ??????????? <!-- 此處可增加其他需要自動創(chuàng)建事務代理的Bean -->

          ??? ??? </property>

          ??????? <!-- 下面定義BeanNameAutoProxyCreator所需的攔截器 -->

          ??????? <property name="interceptorNames">

          ??????????? <list>

          ??????????????? <value>transactionInterceptor</value>

          ??????????????? <!-- 此處可增加其他新的Interceptor -->

          ??????????? </list>

          ??????? </property>

          ??? </bean>

          </beans>

          如果配置文件中僅有兩個目標Bean,可能不能很清楚地看出這種自動創(chuàng)建代理配置方式的優(yōu)勢,但如果有更多目標Bean需要自動創(chuàng)建事務代理,則可以很好地體會到這種配置方式的優(yōu)勢:配置文件只需要簡單地配置目標Bean,然后在BeanNameAutoProxyCreator配置中增加一行即可。

          提示:使用BeanNameAutoProxyCreator可以自動創(chuàng)建事務代理,使用DefaultAdvisor- AutoProxyCreator也可自動創(chuàng)建事務代理。關于后一個Bean后處理器的配置方式,請參看前面6.2.7節(jié)的內容。

          posted on 2009-07-19 10:18 jadmin 閱讀(80) 評論(0)  編輯  收藏

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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 金门县| 航空| 荥阳市| 宁强县| 夏邑县| 吐鲁番市| 乐至县| 兰坪| 湘潭市| 大名县| 赣榆县| 松阳县| 永顺县| 阳江市| 淮滨县| 莱州市| 漠河县| 彩票| 渭源县| 绵阳市| 正阳县| 翁牛特旗| 湟源县| 西平县| 东台市| 永善县| 余干县| 贺兰县| 忻城县| 阳高县| 台山市| 凤翔县| 恭城| 三明市| 资源县| 新津县| 吐鲁番市| 凤山市| 青龙| 芷江| 大姚县|