事務管理是應用系統開發中必不可少的一部分.Spring為事務管理提供了豐富的功能支持.Spring事務管理分為編碼式和聲明式
兩種方式.編碼式事務指的是通過編碼方式實現事務;聲明式事務基於AOP,將具體業務邏輯與事務處理解耦.聲明式事務管理使
業務代碼邏輯不受污染,因此在實際使用中聲明式事務用的比較多.聲明式事務有兩種方式,一種是在配置文件(xml)中做相關事務
規則聲明,另一種是基於@Transactional注解的方式.注解配置是目前流行的使用方式,因此下面將着重介紹基於@Transactional
注解的事務管理.
@Transactional注解管理事務的實現步驟
使用@Transactional注解管理事務的實現步驟分為兩步.第一步,在xml配置文件中添加如下事務配置信息
<tx:annotation-driven /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
也可以通過@EnableTransactionManagement注解也可以啟用事務管理功能.
第二步,將@Transactional注解添加到合適的方法上,並配置合適的屬性信息. @Transactional注解的屬性說明如下:
屬性名 | 說明 |
name | 當在配置文件中有多個TransactionManager,可以使用該屬性指定選擇哪個事務管理器. |
propagation | 事務的傳播行為,默認值REQUIRED. |
isolation | 事務的隔離度,默認值采用DEFAULT |
timeout | 事務的超時時間,默認值為-1.如果超過該時間限制但事務還沒有完成,則自動回滾事務. |
read-only | 指定事務是否為只讀事務,默認值為false;為了忽略那些不需要事務的方法,比如讀取數據,可以設置read-only為true. |
rollbackFor | 用於指定能夠觸發事務回滾的異常類型,如果有多個異常類型需要指定,各類型之間可以通過逗號分隔. |
no-rollback-for | 拋出no-rollback-for指定的異常類型,不回滾事務. |
除此以外,@Transactional注解也可以添加到類級別上.當把@Transactional注解放到類級別時,表示所有該類的公共方法都配置
相同的事務屬性.當類級別配置了@Transactional,方法級別也配置了@Transactional,應用程序會以方法級別的事務屬性信息來
管理事務,方法級別的事務屬性信息會覆蓋類級別的相關配置信息.
Spring的注解方式事務實現機制
在應用系統調用聲明@Transactional的目標方法時,Spring Framework默認使用AOP代理,在代碼運行時生成一個代理對象,根據
@Transactional的屬性配置信息,這個代理對象決定該聲明@Transactional的目標方法是否由攔截器TransactionInterceptor來
使用攔截,在TransactionInterceptor攔截時,會在目標方法開始執行之前創建並加入事務,並執行目標方法的邏輯,最后根據執行
情況是否出現異常,利用抽象事務管理器AbstractPlatformTransactionManager操作數據源DataSource提交或回滾事務.如下圖
所示:
Spring AOP代理有CglibAopProxy和JdkDynamicAopProxy兩種,對於CglibAopProxy,需要調用其內部類的DynamicAdviseInterceptor
的intercept方法.對於JdkDynamicAopProxy,需要調用invoke方法.
正如上文提到的,事務管理的框架是由抽象事務管理器(AbstractPlatformTransactionManager)來提供的,而具體的底層事務處理實現,由
PlatformTransactionManager的具體實現類來實現,如事務管理器DataSourceTransactionManager管理JDBC的Connection.其類結構:

在使用注解方式的事務使用注意事項
正確設置@Transactional的propagation屬性
需要注意下面三種propagation可以不啟動事務,本來期望目標方法進行事務管理,但若是錯誤的配置這三種propagation,事務將
不會發生回滾.
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行.
- TransactionDefinition.PROPAGATION_NOT_SUPPORTS:以非事務方式運行,如果當前存在事務,則把當前事務掛起.
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常.
正確設置@Transactional的rollbackFor屬性
默認情況下,如果在事務中拋出了未檢查異常(繼承自RuntimeException的異常)或者Error,則Spring將回滾事務;除此之外,Spring不會
回滾事務.如果在事務中拋出其他類型的異常,並期望Spring能夠回滾事務,可以指定rollbackFor.通過分析Spring源碼可以知道,若在目標
方法中拋出的異常時rollbackFor指定異常的子類,事務同樣會回滾 .RollbackRuleAttribute的getDepth方法:
private int getDepth(Class<?> exceptionClass, int depth) { if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass == Throwable.class) { return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); }
@Transactional只能應用到public方法才有效
只有@Transactional注解應用到public方法,才能進行事務管理.這是因為在使用Spring AOP代理時,Spring在使用TransactionInterceptor攔截
目標方法執行前后時,DynamicAdvisedInterceptor(CglibAopProxy 的內部類)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會間接調用
AbstractFallbackTransactionAttributeSource(Spring 通過這個類獲取表 1. @Transactional 注解的事務屬性配置屬性信息)的 computeTransactionAttribute
方法。
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null;}
這個方法會檢查目標方法的修飾符是不是public,若不是public,就不會獲取@Transactional的屬性配置信息,最終會造成不會用TransactionInterceptor來攔截該
目標方法進行事務管理.
避免Spring的AOP的自調用問題
在Spring的AOP代理下,只有目標方法由外部調用,目標方法才由Spring生成的代理對象來管理,這會造成自調用問題.若同一類中的其他沒有@Transactional注解
的方法內部調用有@Transactional注解的方法,有@Transactional注解的方法的事務被忽略,不會發生回滾.
可以使用AspectJ替代Spring AOP代理解決這兩個問題(public與自調用問題).
------------引自他人博客