聲明式事務是基於代理實現的。最小的力度是方法級。 編程式事務是基於事務模版來作的,具備較高的侵略性,不建議使用。html
根據PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三個核心接口,咱們徹底能夠經過編程的方式來進行事務管理。示例代碼如清單4所示:java
public class BankServiceImpl implements BankService { private BankDao bankDao; // 事務定義 private TransactionDefinition txDefinition; // 事務管理器 private PlatformTransactionManager txManager; ...... public boolean transfer(Long fromId, Long toId, double amount) { // 事務的狀態 TransactionStatus txStatus = txManager.getTransaction(txDefinition); boolean result = false; try { result = bankDao.transfer(fromId, toId, amount); txManager.commit(txStatus); } catch (Exception e) { result = false; txManager.rollback(txStatus); System.out.println("Transfer Error!"); } return result; } }
清單5. 基於底層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>spring
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; } }); } }
相應的XML配置以下:express
清單 7. 基於 TransactionTemplate 的事務管理示例配置文件 <bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>編程
清單 8. 基於 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>學習
清單9. 基於 TransactionProxyFactoryBean 的事務管理示例配置文件 <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>編碼
清單10. 基於 <tx> 的事務管理示例配置文件 <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>spa
aop:config <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/> <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/> </aop:config> ...... </beans> 若是默認的事務屬性就能知足要求,那麼代碼簡化爲如清單 11 所示:.net
清單 11. 簡化後的基於 <tx> 的事務管理示例配置文件 <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 方法將都具備該類型的事務屬性,同時,咱們也能夠在方法級別使用該標註來覆蓋類級別的定義。如清單12所示:
清單12. 基於 @Transactional 的事務管理示例配置文件 @Transactional(propagation = Propagation.REQUIRED) public boolean transfer(Long fromId, Long toId, double amount) { return bankDao.transfer(fromId, toId, amount); } Spring 使用 BeanPostProcessor 來處理 Bean 中的標註,所以咱們須要在配置文件中做以下聲明來激活該後處理 Bean,如清單13所示:
清單13. 啓用後處理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 的聲明式事務管理方式,可是,學習這兩種方式很是有利於對底層實現的理解。
雖然上面共列舉了四種聲明式事務管理方式,可是這樣的劃分只是爲了便於理解,其實後臺的實現方式是同樣的,只是用戶使用的方式不一樣而已。
基於 TransactionDefinition、PlatformTransactionManager、TransactionStatus 編程式事務管理是 Spring 提供的最原始的方式,一般咱們不會這麼寫,可是瞭解這種方式對理解 Spring 事務管理的本質有很大做用。
基於 TransactionTemplate 的編程式事務管理是對上一種方式的封裝,使得編碼更簡單、清晰。
基於 TransactionInterceptor 的聲明式事務是 Spring 聲明式事務的基礎,一般也不建議使用這種方式,可是與前面同樣,瞭解這種方式對理解 Spring 聲明式事務有很大做用。
基於 TransactionProxyFactoryBean 的聲明式事務是上中方式的改進版本,簡化的配置文件的書寫,這是 Spring 早期推薦的聲明式事務管理方式,可是在 Spring 2.0 中已經不推薦了。
基於 <tx> 和 <aop> 命名空間的聲明式事務管理是目前推薦的方式,其最大特色是與 Spring AOP 結合緊密,能夠充分利用切點表達式的強大支持,使得管理事務更加靈活。
基於 @Transactional 的方式將聲明式事務管理簡化到了極致。開發人員只需在配置文件中加上一行啓用相關後處理 Bean 的配置,而後在須要實施事務管理的方法或者類上使用 @Transactional 指定事務規則便可實現事務管理,並且功能也沒必要其餘方式遜色。