Spring事務@Transactional標簽深入學習


事務管理是應用系統開發中必不可少的一部分.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提交或回滾事務.如下圖

所示:

image001

Spring AOP代理有CglibAopProxy和JdkDynamicAopProxy兩種,對於CglibAopProxy,需要調用其內部類的DynamicAdviseInterceptor

的intercept方法.對於JdkDynamicAopProxy,需要調用invoke方法.

正如上文提到的,事務管理的框架是由抽象事務管理器(AbstractPlatformTransactionManager)來提供的,而具體的底層事務處理實現,由

PlatformTransactionManager的具體實現類來實現,如事務管理器DataSourceTransactionManager管理JDBC的Connection.其類結構:


image002

在使用注解方式的事務使用注意事項

正確設置@Transactional的propagation屬性

需要注意下面三種propagation可以不啟動事務,本來期望目標方法進行事務管理,但若是錯誤的配置這三種propagation,事務將

不會發生回滾.

  1. TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行.
  2. TransactionDefinition.PROPAGATION_NOT_SUPPORTS:以非事務方式運行,如果當前存在事務,則把當前事務掛起.
  3. 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與自調用問題).

------------引自他人博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM