Spring 2.X的事務配置策略java
雖然前面介紹的TransactionProxyFactoryBean配置策略簡單易懂,但配置起來極爲麻煩:每一個目標Bean都須要配置一個TransactionProxyFactoryBean代理,這種方式將致使配置文件急劇增長。mysql
Spring 2.X的XMLSchema方式提供了更簡潔的事務配置策略,Spring 2.X提供了tx命名空間來配置事務管理,tx命名空間提供了<tx:advice../>元素來配置事務加強處理,一旦使用該元素配置了加強處理,就能夠直接使用<aop:advisor../>元素啓用自動代理了。
spring
下面的應用示例依然使用前面(點擊查看)提供的NewsDao接口和NewsDaoImpl實現類,但改成使用<tx:advice../>元素來配置事務加強處理,再使用<tx:advisor../>爲容器中一批Bean配置自動事務代理。下面是配置文件:
sql
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/aop/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 使用C3P0數據庫鏈接池做爲數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPolledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost/test" /> <property name="user" value="root" /> <property name="password" value="root" /> <property name="maxPoolSize" value="40" /> <property name="minPoolSize" value="4" /> <property name="initialPoolSize" value="10" /> <!-- 指定數據庫鏈接池的鏈接的最大空閒時間 --> <property name="maxIdleTime" value="20" /> </bean> <!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager類,該類實現了 PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 配置TransactionManager時須要注入數據源引用 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 下面這個是前面定義的業務Bean --> <bean id="newsDao" class="com.abc.dao.impl.NewsDaoImpl"> <!-- 爲業務Bean注入屬性 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置事務加強處理Bean,指定事務管理器 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 用於配置詳細的事務語義 --> <tx:attributes> <!-- 全部以開頭的方法都是read-only的,出現異常時回滾數據庫 --> <tx:method="get*" read-only="true" rollback-for="java.lang.Exception"/> <!-- 其餘方法使用默認的事務設置 --> <tx:method="*" /> </tx:attributes> </tx:advice> <!-- AOP配置的元素 --> <aop:config> <!-- 切點,匹配com.abc.dao.impl包下的全部以impl結尾的類裏的全部方法的執行 --> <aop:pointcut id="myPointcut" expression="(* com.abc.dao.impl.*Impl.*(..))" /> <!-- 指定在txAdvice切點,使用txAdvice事務加強處理 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> </aop:config> </beans>
上面的配置文件中,在XMLSchema中啓用了Spring配置文件的tx、aop兩個命名空間,後面配置了一個事務加強處理,配置<tx:advice../>元素時只須要指定一個transaction-manager屬性,該屬性的默認值是"transactionManager"【提示:若是事務管理器Bean(PlatformTransactionManager的實現類)的名字是transactionManager,則配置<tx:advice../>元素時能夠省略指定transaction-manager屬性。只有當咱們爲事務管理器Bean指定了其餘名字時,才須要爲<tx:advice../>元素指定transaction-manager屬性】。
數據庫
配置文件最後一段是<aop:config../>的定義,它確保由txAdvice切面定義事務加強功能能在合適的點被織入。首先咱們定義一個切點,命名爲myPointcut,而後用一個Advisor把這個切入點與txAdvice綁定在一塊兒,表示當myPointcut執行時,txAdvice定義的加強處理將被織入。
express
<aop:advisor../>元素是一個很奇怪的東西,它用於配置一個Advisor,但標準的 AOP 概念裏並無所謂的"Advisor",Advisor是Spring 1.X遺留下來的一個東西,Advisor的做用很是簡單:將加強處理方法(Advice)和切入點(Pointcut)綁定在一塊兒,保證Advice所包含的加強處理將在對應的切點方法執行時被織入。
安全
當咱們使用這種配置策略時,無需爲每一個業務Bean專門配置事務代理,Spring AOP會爲業務組件自動生成代理,程序能夠直接請求容器中的newsDao Bean,該Bean的方法已經具備了事務性——由於該Bean的實現類位於com.abc.dao.impl包下,並且類名以Impl結尾,和myPointcut切點的表達式匹配,故在指定裏面的方法時,事務將被自動織入。下面是主程序:spa
public class SpringTest { public static void main(String[] arg) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); NewsDao dao = (NewsDao)context.getBean("newsDao",NewsDao.class); dao.insert("新聞標題","新聞內容"); } }
上面的程序中直接獲取Spring中的NewsDao,並調用其insert方法,由於Spring AOP會爲該Bean自動織入事務加強處理方式,因此newsDao Bean裏的全部方法都具備事務性。
.net
採用這種方式來配置事務還有一個額外的優點:Spring容器中只有一個newsDao,該newsDao已經具備了事務性,不像採用TransactionProxyFactoryBean策略時,容器中有一個目標Bean,還有爲該目標Bean配置的事務代理Bean——當程序"不當心"獲取了目標Bean後,若是調用目標Bean,那麼此時Bean的方法時不具有事務性的,這可能埋下安全隱患。
代理
採用<tx:advisor../>元素將Advice和切點綁定時,其實是由Spring提供的Bean後處理器完成的。Spring提供了BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator兩個Bean後處理器,它們均可之後處理容器中的Bean(爲它們織入切面中包含的加強處理)。前面咱們配置<aop:advisor../>元素時傳入一個txAdvice事務加強處理,因此Bean後處理器將全部Bean實例裏匹配切入點的方法織入事務操做的加強處理。
配置<tx:advice../>元素時除了須要指定一個transaction-manager屬性之外,重要的是須要配置一個<attributes../>子元素,該子元素又能夠包含多個<method../>元素。其實,配置<tx:advice../>元素的重點就是配置<method../>元素,每一個<method../>子元素爲一批方法指定所需的事務語義,包括事務傳播屬性、事務隔離屬性、事務超時屬性、只讀事務、對指定異常回滾、對指定異常不回滾等。下面介紹<method../>元素能夠指定的幾個屬性:
name:必選屬性,與該事物語義關聯的方法名。支持使用通配符,例如"get*","handle*","save*","on*Event"等
propagation:指定事務的傳播行爲,該屬性值能夠是Propagation枚舉類的值的任意一種,各枚舉值看這裏
isolation:指定事務的隔離級別,該屬性值能夠是Isolation枚舉類的值的任意一種,各枚舉值的具體含義請參考API文檔。該屬性的默認值爲Isolation.DEFAULT
timeout:指定事務超時時間(單位:秒)指定-1表示不會超時。該屬性的默認值爲-1
read-only:指定事務是否只讀。該屬性默認值爲false
rollback-for:指定觸發事務回滾的異常類(使用全限定名),該屬性能夠指定多個異常類,多個異常類用英文逗號隔開
no-rollback-for:指定不觸發事務回滾的異常類(使用全限定名),該屬性能夠指定多個異常類,多個異常類用英文逗號隔開
若是咱們想讓事務在遇到特定的Checked異常時自動回滾,則可藉助於rollback-for屬性,例如:
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 全部以開頭的方法都是read-only的,出現異常時回滾數據庫 --> <tx:method="get*" read-only="true" rollback-for="java.lang.Exception"/> </tx:attributes> </tx:advice>
若是咱們想讓事務在出現某些特定的異常時不回滾,可以使用no-rollback-for屬性,例如:
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 出現某些特定的Exception,不回滾 --> <tx:method="get*" read-only="true" no-rollback-for="com.abc.exception.XxxException"/> </tx:attributes> </tx:advice>
基於@Transactional註解
Spring除了支持XMLSchema方式配置事務之外,還支持使用@Transactional註解來配置事務。該註解既能夠修飾Bean類,也能夠修飾Bean方法。修飾類時,代表這些事務設置對整個Bean都起做用;修飾方法時,代表事務設置只對該方法有效。
和使用配置文件相似,使用@Transactional註解時,須要指定如下屬性:
isolation:事務隔離級別。默認爲底層事務的隔離級別
noRollbackFor:遇到指定異常時強制不回滾事務
noRollbackForClassName:遇到指定多個異常時強制不回滾事務,該屬性值能夠指定多個異常類名
propagation:事務傳播屬性
readOnly:事務是否只讀
rollbackFor:遇到指定異常時強制回滾事務
rollbackForClassName:遇到指定異常時強制回滾事務,該屬性值能夠指定多個異常類名
timeout:事務的超時時長
下面使用@Transactional修飾須要添加事務的方法:
public class NewsDaoImpl implements NewsDao { @Transactional(propagation=Propagation.REQUIRED) public void insert(String title, String content) { //... } }
上面的Bean類中insert方法使用了@Transactional修飾,代表該方法就會具備事務性。僅使用這個註解配置還不夠,還須要在讓Spring根據註解來配置事務。因此還須要在Spring的配置文件中增長以下配置片斷:
<!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager類,該類實現了 PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 配置TransactionManager時須要注入數據源引用 --> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />