1、事物的定義和特性java
事物表明的是一個操做集合。也就是一連串的操做爲一個最小單元不可分割(原子性),這一連串的操做要麼所有成功要不所有失敗(一致性),每個操做集合互不干擾(隔離性),操做集合的全部操做完成後數據必須以一種持久性方式存儲起來(持久性)。spring
2、Spring的事務管理express
Spring的事務管理分爲編程式事物和聲明式事物。編程
一、編程式事物管理框架
基於底層API的編程式事物管理學習
根據PlateformTransactionManger、TransactionDefinition和TransactionStatus這三個核心接口完成 編程式事物管理。編碼
基於底層API的事物管理代碼示例spa
1 public class BankServiceImpl implements BankService { 2 private BankDao bankDao; 3 private TransactionDefinition txDefinition; 4 private PlatformTransactionManager txManager; 5 ...... 6 public boolean transfer(Long fromId, Long toId, double amount) { 7 TransactionStatus txStatus = txManager.getTransaction(txDefinition); 8 boolean result = false; 9 try { 10 result = bankDao.transfer(fromId, toId, amount); 11 txManager.commit(txStatus); 12 } catch (Exception e) { 13 result = false; 14 txManager.rollback(txStatus); 15 System.out.println("Transfer Error!"); 16 } 17 return result; 18 } 19 }
基於底層API的配置文件代碼示例代理
<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="txManager" ref="transactionManager"/> <property name="txDefinition"> <bean class="org.springframework.transaction.support.DefaultTransactionDefinition"> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> </bean> </property> </bean>
基於TransactionTemplate的編程式事物管理code
這種是上面的簡化版
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer(final Long fromId, final Long toId, final double amount) { return (Boolean) transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false; System.out.println("Transfer Error!"); } return result; } }); } }
配置文件示例
<bean id="bankService"
class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
TransactionTemplate 的 execute() 方法有一個 TransactionCallback 類型的參數,該接口中定義了一個 doInTransaction() 方法,一般咱們以匿名內部類的方式實現 TransactionCallback 接口,並在其 doInTransaction() 方法中書寫業務邏輯代碼。這裏可使用默認的事務提交和回滾規則,這樣在業務代碼中就不須要顯式調用任何事務管理的 API。doInTransaction() 方法有一個TransactionStatus 類型的參數,咱們能夠在方法的任何位置調用該參數的 setRollbackOnly() 方法將事務標識爲回滾的,以執行事務回滾。
根據默認規則,若是在執行回調方法的過程當中拋出了未檢查異常,或者顯式調用了TransacationStatus.setRollbackOnly() 方法,則回滾事務;若是事務執行完成或者拋出了 checked 類型的異常,則提交事務。
TransactionCallback 接口有一個子接口 TransactionCallbackWithoutResult,該接口中定義了一個 doInTransactionWithoutResult() 方法,TransactionCallbackWithoutResult 接口主要用於事務過程當中不須要返回值的狀況。固然,對於不須要返回值的狀況,咱們仍然可使用 TransactionCallback 接口,並在方法中返回任意值便可。
二、聲明式事物管理
Spring 的聲明式事務管理在底層是創建在 AOP 的基礎之上的。其本質是對方法先後進行攔截,而後在目標方法開始以前建立或者加入一個事務,在執行完目標方法以後根據執行狀況提交或者回滾事務。
聲明式事務最大的優勢就是不須要經過編程的方式管理事務,這樣就不須要在業務邏輯代碼中摻瑣事務管理的代碼,只需在配置文件中作相關的事務規則聲明(或經過等價的基於標註的方式),即可以將事務規則應用到業務邏輯中。由於事務管理自己就是一個典型的橫切邏輯,正是 AOP 的用武之地。Spring 開發團隊也意識到了這一點,爲聲明式事務提供了簡單而強大的支持。
聲明式事務管理曾經是 EJB 引覺得傲的一個亮點,現在 Spring 讓 POJO 在事務管理方面也擁有了和 EJB 同樣的待遇,讓開發人員在 EJB 容器以外也用上了強大的聲明式事務管理功能,這主要得益於 Spring 依賴注入容器和 Spring AOP 的支持。依賴注入容器爲聲明式事務管理提供了基礎設施,使得 Bean 對於 Spring 框架而言是可管理的;而 Spring AOP 則是聲明式事務管理的直接實現者,這一點經過清單8能夠看出來。
一般狀況下,筆者強烈建議在開發中使用聲明式事務,不只由於其簡單,更主要是由於這樣使得純業務代碼不被污染,極大方便後期的代碼維護。
和編程式事務相比,聲明式事務惟一不足地方是,後者的最細粒度只能做用到方法級別,沒法作到像編程式事務那樣能夠做用到代碼塊級別。可是即使有這樣的需求,也存在不少變通的方法,好比,能夠將須要進行事務管理的代碼塊獨立爲方法等等。
下面就來看看 Spring 爲咱們提供的聲明式事務管理功能。
Spring 提供了 TransactionInterceptor 類來實施聲明式事務管理功能。
<beans...> ...... <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="bankServiceTarget"/> <property name="interceptorNames"> <list> <idref bean="transactionInterceptor"/> </list> </property> </bean> ...... </beans>
<beans......> ...... <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="bankServiceTarget"/> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> ...... </beans>
前面兩種聲明式事務配置方式奠基了 Spring 聲明式事務管理的基石。在此基礎上,Spring 2.x 引入了 <tx> 命名空間,結合使用 <aop> 命名空間,帶給開發人員配置聲明式事務的全新體驗,配置變得更加簡單和靈活。另外,得益於 <aop> 命名空間的切點表達式支持,聲明式事務也變得更增強大。
<beans......> ...... <bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <tx:advice id="bankAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/> <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/> </aop:config> ...... </beans>
若是默認的事務屬性就能知足要求,那麼代碼簡化爲
<beans......> ...... <bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <tx:advice id="bankAdvice" transaction-manager="transactionManager"> <aop:config> <aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/> <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/> </aop:config> ...... </beans>
因爲使用了切點表達式,咱們就不須要針對每個業務類建立一個代理對象了。另外,若是配置的事務管理器 Bean 的名字取值爲「transactionManager」,則咱們能夠省略 <tx:advice> 的 transaction-manager 屬性,由於該屬性的默認值即爲「transactionManager」。
除了基於命名空間的事務配置方式,Spring 2.x 還引入了基於 Annotation 的方式,具體主要涉及@Transactional 標註。@Transactional 能夠做用於接口、接口方法、類以及類方法上。看成用於類上時,該類的全部 public 方法將都具備該類型的事務屬性,同時,咱們也能夠在方法級別使用該標註來覆蓋類級別的定義。
@Transactional(propagation = Propagation.REQUIRED) public boolean transfer(Long fromId, Long toId, double amount) { return bankDao.transfer(fromId, toId, amount); }
Spring 使用 BeanPostProcessor 來處理 Bean 中的標註,所以咱們須要在配置文件中做以下聲明來激活該後處理 Bean
啓用處理Bean
<tx:annotation-driven transaction-manager="transactionManager"/>
與前面類似,transaction-manager 屬性的默認值是 transactionManager,若是事務管理器 Bean 的名字即爲該值,則能夠省略該屬性。
雖然 @Transactional 註解能夠做用於接口、接口方法、類以及類方法上,可是 Spring 小組建議不要在接口或者接口方法上使用該註解,由於這隻有在使用基於接口的代理時它纔會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。若是你在 protected、private 或者默承認見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。
基於 <tx> 命名空間和基於 @Transactional 的事務聲明方式各有優缺點。基於 <tx> 的方式,其優勢是與切點表達式結合,功能強大。利用切點表達式,一個配置能夠匹配多個方法,而基於 @Transactional 的方式必須在每個須要使用事務的方法或者類上用 @Transactional 標註,儘管可能大多數事務的規則是一致的,可是對 @Transactional 而言,也沒法重用,必須逐個指定。另外一方面,基於 @Transactional 的方式使用起來很是簡單明瞭,沒有學習成本。開發人員能夠根據須要,任選其中一種使用,甚至也能夠根據須要混合使用這兩種方式。
若是不是對遺留代碼進行維護,則不建議再使用基於 TransactionInterceptor 以及基於TransactionProxyFactoryBean 的聲明式事務管理方式,可是,學習這兩種方式很是有利於對底層實現的理解。