spring事務(wù)管理

對大多數(shù)Java開發(fā)者來說躯肌,Spring事務(wù)管理是Spring應(yīng)用中最常用的功能者春,使用也比較簡單。本文主要從三個(gè)方面(基本概述清女、基于源碼的原理分析以及需要注意的細(xì)節(jié))來逐步介紹Spring事務(wù)管理的相關(guān)知識點(diǎn)及原理钱烟,作為Spring事務(wù)管理的學(xué)習(xí)總結(jié)。

Spring事務(wù)管理概述

一嫡丙、幾個(gè)重要概念

1.事務(wù)隔離級別

ANSI/ISO SQL92標(biāo)準(zhǔn)定義了4個(gè)隔離級別READ UNCOMMITED拴袭、READ COMMITEDREPEATABLE READSERIALIZABLE迄沫,隔離程度由弱到強(qiáng)稻扬。不同的事務(wù)隔離級別能夠解決數(shù)據(jù)并發(fā)問題的能力不同,它與數(shù)據(jù)庫并發(fā)性是對立的羊瘩,兩者此消彼長泰佳。

2.事務(wù)傳播行為

事務(wù)傳播主要是為了描述兩個(gè)服務(wù)接口方法嵌套調(diào)用時(shí),被調(diào)用者在調(diào)用者有無事務(wù)時(shí)所采取的事務(wù)行為尘吗。Spring框架在TransactionDefinition接口中固定了7種事務(wù)傳播行為:PROPAGATION_REQUIRED逝她、 PROPAGATION_SUPPORTSPROPAGATION_MANDATORY睬捶、 PROPAGATION_REQUIRES_NEW黔宛、 PROPAGATION_NOT_SUPPORTEDPROPAGATION_NEVER擒贸、 PROPAGATION_NESTED臀晃。前面的6種是從EJB中引入的,而PROPAGATION_NESTED是Spring特有的介劫。具體可參見深入淺出事務(wù)(4):Spring事務(wù)的傳播行為徽惋,該文結(jié)合具體代碼示例,通俗易懂座韵。

3.事務(wù)同步管理器

TransactionSynchronizationManager——事務(wù)管理的基石险绘,主要是為了解決事務(wù)管理在多線程環(huán)境下資源(如Connection、Session等)的并發(fā)訪問問題:使用ThreadLocal為不同事務(wù)線程維護(hù)獨(dú)立的資源副本誉碴,以及事務(wù)配置屬性和運(yùn)行狀態(tài)信息宦棺,使各個(gè)事務(wù)線程互不影響。

4.事務(wù)管理SPI

SPI(Service Provider Interface)是一個(gè)框架開放給第三方的可擴(kuò)展服務(wù)接口黔帕,供其具體實(shí)現(xiàn)代咸,以支持框架的擴(kuò)展性和插件式組件。Spring事務(wù)管理SPI主要包括3個(gè)接口:PlatformTransactionManager(進(jìn)行事務(wù)的創(chuàng)建成黄、提交或回滾)侣背、TransactionDefinition(定義事務(wù)屬性白华,如隔離級別)和TransactionStatus(事務(wù)運(yùn)行時(shí)狀態(tài),如是否已完成)贩耐。這三者通過PlatformTransactionManager的如下接口進(jìn)行關(guān)聯(lián):

// 根據(jù)事務(wù)定義創(chuàng)建事務(wù),并由TransactionStatus表示它
TransactionStatus getTransaction(TransactionDefinition definition);
// 根據(jù)事務(wù)運(yùn)行時(shí)狀態(tài)提交或回滾事務(wù)
void commit(TransactionStatus status);
void rollback(TransactionStatus status);

二厦取、基本用法

三種方式:編程潮太、XML配置和注解。第一方式對應(yīng)用代碼侵入性較大虾攻,現(xiàn)已較少使用铡买。后面兩種則都屬于聲明式事務(wù)管理的方式,兩者的共同點(diǎn)是都提供事務(wù)管理信息的元數(shù)據(jù)霎箍,只不過方式不同奇钞。前者對代碼的侵入性最小,也最為常用漂坏,后者則屬于較為折衷的方案景埃,有一點(diǎn)侵入性,但相對也較少了配置顶别,各有優(yōu)劣谷徙,依場景需求而定聲明式事務(wù)管理是Spring的一大亮點(diǎn)驯绎,利用AOP技術(shù)將事務(wù)管理作為切面動態(tài)織入到目標(biāo)業(yè)務(wù)方法中完慧,讓事務(wù)管理簡單易行。

而不管是使用哪種方式剩失,數(shù)據(jù)源屈尼、事務(wù)管理器都是必須的,一般通過XML的Bean配置:

...
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destory-method="close">
  <property name="driverClass"><value>${jdbc.driverClass}</value></property>
  <property name="jdbcUrl"><value>${jdbc.jdbcUrl}</value></property>
  <property name="user"><value>${jdbc.username}</value></property>
  <property name="password"><value>${jdbc.password}</value></property>
  <property name="maxPoolSize"><value>${jdbc.maxPoolSize}</value></property>
  <property name="minPoolSize"><value>${jdbc.minPoolSize}</value></property>
  <property name="initialPoolSize"><value>${initialPoolSize}</value></property>
</bean>

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource">
    <ref local="dataSource" />
  </property>
  <!-- 指定事務(wù)管理器標(biāo)識拴孤,可被@Transactional注解引用 -->
  <qualifier value="txManagerA" />
</bean>
...

1.編程式事務(wù)管理

采用與DAO模板類一樣的開閉思想脾歧,Spring提供了線程安全的TransactionTemplate模板類來處理不變的事務(wù)管理邏輯,將變化的部分抽象為回調(diào)接口TransactionCallback供用戶自定義數(shù)據(jù)訪問邏輯乞巧。使用示例:

public class ServiceAImpl {
    private DaoA daoA;
    @autowried
    private TransactionTemplate template;
    public void addElement(final Element ele) {
        template.execute(new TransactionCallbackWithoutResult() {
            protected void doInTransactionWithoutResult(TransactionStatus status){
                daoA.addElement(ele);
            }
        });
    }
}

TransactionTemplate的配置信息:

...
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
  <property name="transactionManager">
    <ref local="txManager" />
  </property>
  <property name="isolationLevelName" value="ISOLATION_DEFAULT" />
  <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
</bean>
...

當(dāng)然涨椒,用戶也可以不使用TransactionTemplate,而是直接基于原始的Spring事務(wù)管理SPI進(jìn)行編程式事務(wù)管理绽媒,只不過這種方式對代碼侵入性最大蚕冬,不推薦使用,這里也就不多做介紹了是辕。

2.基于XML配置的事務(wù)管理

Spring早期版本囤热,是通過TransactionProxyFactoryBean代理類實(shí)施聲明式事務(wù)配置,由于這種方式的種種弊端获三,后來引入AOP切面描述語言后旁蔼,提出一種更簡潔的基于Schema的配置方式:tx/aop命名空間锨苏,使聲明式事務(wù)配置更簡潔便利。

2.1基于Bean的配置

...
<bean id="serviceATarget" class="org.sherlocky.book.spring3x.service.ServiceAImpl" />
<bean id="serviceA" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
      p:transactionManager-ref="txManager"
      p:target-ref="serviceATarget">
  <property name="transactionAttributes">
    <props>
      <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>
...

2.1基于Schema的配置(常用)

...
<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="create*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
    <tx:method name="delete*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
    <tx:method name="update*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
    <tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="300" />
    <tx:method name="*" propagation="REQUIRED" read-only="true" timeout="300" />
  </tx:attributes>
</tx:advice>

<aop:config>
  <aop:pointcut id="txPointcut" expression="execution(* org.sherlocky.book.spring3x.service.*ServiceA.*(..))" />
  <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>

3.基于注解的事務(wù)管理

通過@Transactional對需要事務(wù)增強(qiáng)的Bean接口棺聊、實(shí)現(xiàn)類或方法進(jìn)行標(biāo)注伞租,在容器中配置<tx:annotation-driven>以啟用基于注解的聲明式事務(wù)。注解所提供的事務(wù)屬性信息與XML配置中的事務(wù)信息基本一致限佩,只不過是另一種形式的元數(shù)據(jù)而已葵诈。使用示例:

@Transactional("txManagerA")
public class ServiceAImpl {
    private DaoA daoA;
    public void addElement(final Element ele) {
        ...
    }
}

Spring事務(wù)管理源碼分析-(spring3.1.0)

源碼分析一定要有目的性,至少有一條清晰的主線祟同,比如要搞清楚框架的某一個(gè)功能點(diǎn)背后的代碼組織作喘,前因后果,而不是一頭扎進(jìn)源碼里晕城,無的放矢泞坦。本文就從Spring事務(wù)管理的三種使用方式入手,逐個(gè)分析Spring在背后都為我們做了些什么砖顷。

一贰锁、編程式

1.TransactionTemplate

TransactionTemplate是編程式事務(wù)管理的入口,源碼如下:

public class TransactionTemplate extends DefaultTransactionDefinition
        implements TransactionOperations, InitializingBean {
    private PlatformTransactionManager transactionManager;
    ...
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);【1】
        }
        else {
            TransactionStatus status = this.transactionManager.getTransaction(this);【2】
            T result;
            try {
                result = action.doInTransaction(status);
            }
            catch (RuntimeException ex) {【3】
                // Transactional code threw application exception -> rollback
                rollbackOnException(status, ex);
                throw ex;
            }
            catch (Error err) {【4】
                // Transactional code threw error -> rollback
                rollbackOnException(status, err);
                throw err;
            }
            catch (Exception ex) {【5】
                // Transactional code threw unexpected exception -> rollback
                rollbackOnException(status, ex);
                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
            }
            this.transactionManager.commit(status);
            return result;
        }
    }
}

1.1整體概述

TransactionTemplate提供了唯一的編程入口execute择吊,它接受用于封裝業(yè)務(wù)邏輯的TransactionCallback接口的實(shí)例李根,返回用戶自定義的事務(wù)操作結(jié)果T。具體邏輯:先是判斷transactionManager是否是接口CallbackPreferringPlatformTransactionManager的實(shí)例几睛,若是則直接委托給該接口的execute方法進(jìn)行事務(wù)管理房轿;否則交給它的核心成員PlatformTransactionManager進(jìn)行事務(wù)的創(chuàng)建、提交或回滾操作所森。

CallbackPreferringPlatformTransactionManger接口擴(kuò)展自PlatformTransactionManger囱持,根據(jù)以下的官方源碼注釋可知,該接口相當(dāng)于是把事務(wù)的創(chuàng)建焕济、提交和回滾操作都封裝起來了纷妆,用戶只需要傳入TransactionCallback接口實(shí)例即可,而不是像使用PlatformTransactionManger接口那樣晴弃,還需要用戶自己顯示調(diào)用getTransaction掩幢、rollback或commit進(jìn)行事務(wù)管理。

 // Implementors of this interface automatically express a preference for
 // callbacks over programmatic getTransaction, commit and rollback calls.
 interface CallbackPreferringPlatformTransactionManager extends PlatformTransactionManager{...}

1.2具體剖析

DefaultTransactionDefinition

可以看到transactionTemplate直接擴(kuò)展自DefaultTransactionDefinition上鞠,讓自身具有默認(rèn)事務(wù)定義功能际邻,【1】和【2】處將this作為execute或getTransaction的實(shí)參傳入,說明該事務(wù)管理是采用默認(rèn)的事務(wù)配置芍阎,可以看下DefaultTransactionDefinition中定義的默認(rèn)配置:

...
private int propagationBehavior = PROPAGATION_REQUIRED; //常用選擇:當(dāng)前沒有事務(wù)世曾,則新建;否則加入到該事務(wù)中
private int isolationLevel = ISOLATION_DEFAULT;         //使用數(shù)據(jù)庫默認(rèn)的隔離級別
private int timeout = TIMEOUT_DEFAULT;                  //-1谴咸,使用數(shù)據(jù)庫的超時(shí)設(shè)置
private boolean readOnly = false;                       //非只讀事務(wù)
TransactionOperations和InitializingBean

而TransactionOperations和InitializingBean接口分別定義了如下單個(gè)方法轮听。InitializingBean是Spring在初始化所管理的Bean時(shí)常用的接口骗露,以確保某些屬性被正確的設(shè)置或做一些初始化時(shí)的后處理操作,可參考InitializingBean的作用血巍。

<T> T execute(TransactionCallback<T> action);   //TransactionTemplate的編程接口
void afterPropertiesSet();                      //Bean初始化時(shí)調(diào)用:在成員變量裝配之后

TransactionTemplate實(shí)現(xiàn)InitializingBean接口萧锉,主要是確保其核心成員transactionManager是否已初始化:

public void afterPropertiesSet() {
    if (this.transactionManager == null) {
        throw new IllegalArgumentException("Property 'transactionManager' is required");
    }
}

從【3】【4】【5】可看出,基于TransactionTemplate的事務(wù)管理藻茂,在發(fā)生RuntimeException驹暑、Error或Exception時(shí)都會回滾,正常時(shí)才提交事務(wù)辨赐。

2. PlatformTransactionManager

該接口在Spring事務(wù)管理中扮演著重要角色【┌欤看下getTransaction的源碼注釋:

// Return a currently active transaction or create a new one, according to
// the specified propagation behavior.

該方法的主要作用就是根據(jù)TransactionDefinition返回當(dāng)前有效事務(wù)或新建事務(wù)掀序,其中就包含了事務(wù)傳播行為的控制邏輯。其唯一實(shí)現(xiàn)就是該接口對應(yīng)的抽象類AbstractPlatformTransactionManager惭婿,這是典型的接口->抽象類->具體實(shí)現(xiàn)類三層結(jié)構(gòu)不恭,以提高代碼復(fù)用性。其中抽象類是負(fù)責(zé)實(shí)現(xiàn)一些共有邏輯财饥,而具體子類則是各自實(shí)現(xiàn)差異化功能:

// 聲明為final换吧,確保不能再被子類重寫
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();
        ...
        if (isExistingTransaction(transaction)) {【1】
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }
        ...
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY){
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);【2】
            ...
            try {
                ...
            }
            catch (RuntimeException ex) {
                resume(null, suspendedResources);【3】
                throw ex;
            }
            catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        }
        else {
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

可以看到它會根據(jù)【1】處的isExistingTransaction方法判斷當(dāng)前是否有事務(wù)而分別作出不同的處理,包括掛起和恢復(fù)當(dāng)前事務(wù)等钥星,有興趣的童鞋可以深入【2】處的supend和【3】處的resume方法沾瓦,會發(fā)現(xiàn)對事務(wù)的掛起和恢復(fù)操作實(shí)際是委托于TransactionSynchronizationManager來做的,而該類在前面也提過到谦炒,是Spring管理事務(wù)資源的贯莺,這幾個(gè)重要接口和類的關(guān)系漸漸清晰了,由于篇幅有限宁改,后面打算單獨(dú)另起一篇細(xì)講缕探。

2.聲明式

基于XML和注解的方式都是屬于聲明式事務(wù)管理,只是提供元數(shù)據(jù)的形式不用还蹲,索性就一起講了爹耗。聲明式事務(wù)的核心實(shí)現(xiàn)就是利用AOP技術(shù),將事務(wù)邏輯作為環(huán)繞增強(qiáng)MethodInterceptor動態(tài)織入目標(biāo)業(yè)務(wù)方法中谜喊。其中的核心類為TransactionInterceptor潭兽。從以下代碼注釋可知,TransactionInterceptor是專用于聲明式事務(wù)管理的锅论。

// AOP Alliance MethodInterceptor for declarative transaction
// management using the common Spring transaction infrastructure {PlatformTransactionManager}
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {...}

1.TransactionInterceptor

// AOP Alliance MethodInterceptor for declarative transaction
// management using the common Spring transaction infrastructure
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {...}

從上述注釋中可知該類是專用于聲明式事務(wù)管理的讼溺,它的核心方法如下invoke:

public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // If the transaction attribute is null, the method is non-transactional.
        final TransactionAttribute txAttr =
                getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);【1】
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);【2】
        final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);

        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);【3】
            Object retVal = null;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                retVal = invocation.proceed();
            }
            catch (Throwable ex) {
                // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);【4】
                throw ex;
            }
            finally {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);【5】
            return retVal;
        }
        else {
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                        new TransactionCallback<Object>() {
                            public Object doInTransaction(TransactionStatus status) {
                                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                                try {
                                    return invocation.proceed();
                                }
                                ...
                            }
                        });
                ...
            }
            ...
        }
    }

1.1整體概述

TransactionInterceptor實(shí)現(xiàn)了MethodInterceptor接口,將事務(wù)管理的邏輯封裝在環(huán)繞增強(qiáng)的實(shí)現(xiàn)中最易,而目標(biāo)業(yè)務(wù)代碼則抽象為MethodInvocation(該接口擴(kuò)展自Joinpoint怒坯,故實(shí)際是AOP中的連接點(diǎn))炫狱,使得事務(wù)管理代碼與業(yè)務(wù)邏輯代碼完全分離,可以對任意目標(biāo)類進(jìn)行無侵入性的事務(wù)織入剔猿。具體邏輯:先根據(jù)MethodInvocation獲取事務(wù)屬性TransactionAttribute视译,根據(jù)TransactionAttribute得到對應(yīng)的PlatformTransactionManager,再根據(jù)其是否是CallbackPreferringPlatformTransactionManager的實(shí)例分別做不同的處理归敬,整體上跟TransactionTemplate中大相徑庭酷含,后面主要是介紹幾點(diǎn)不同的地方。

1.2具體剖析

MethodInterceptor

MethodInterceptor是AOP中的環(huán)繞增強(qiáng)接口汪茧,同一個(gè)連接點(diǎn)可以有多個(gè)增強(qiáng)椅亚,而TransactionInterceptor擴(kuò)展自該接口,說明事務(wù)管理只是眾多橫切邏輯中的一種舱污,還有很多其他的呀舔,比如像日志記錄、性能監(jiān)控等扩灯,對于AOP而言并無區(qū)別媚赖,它會按照增強(qiáng)的順序統(tǒng)一處理。關(guān)于AOP珠插,后期會單獨(dú)一篇詳細(xì)介紹惧磺。

TransactionAttribute和TransactionAttributeSource

在代碼【1】處,委托給TransactionAttributeSource根據(jù)MethodInvocation獲取對應(yīng)的事務(wù)屬性TransactionAttribute捻撑,先來看下TransactionAttribute:

public interface TransactionAttribute extends TransactionDefinition {
    /**
     * Return a qualifier value associated with this transaction attribute.
     * <p>This may be used for choosing a corresponding transaction manager
     * to process this specific transaction.
     */
    String getQualifier();
    /**
     * Should we roll back on the given exception?
     * @param ex the exception to evaluate
     * @return whether to perform a rollback or not
     */
    boolean rollbackOn(Throwable ex);   
}

就是在TransactionDefinition的基礎(chǔ)上增加了兩個(gè)可定制屬性磨隘,使用過XML配置和注解方式的童鞋應(yīng)該都對qualifier和rollback-for再熟悉不過了,那兩個(gè)新增屬性就是為了支持這兩個(gè)配置項(xiàng)的布讹。再來看下TransactionAttributeSource:

/**
 * Interface used by TransactionInterceptor. Implementations know
 * how to source transaction attributes, whether from configuration,
 * metadata attributes at source level, or anywhere else.
 * @see TransactionInterceptor#setTransactionAttributeSource
 * @see TransactionProxyFactoryBean#setTransactionAttributeSource
 */
public interface TransactionAttributeSource {
    TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass);
}

之所以有這個(gè)接口琳拭,是因?yàn)镾pring提供了XML配置、注解等不同的事務(wù)元數(shù)據(jù)形式描验,即事務(wù)屬性的來源多樣白嘁,該接口正是將事務(wù)配置的來源進(jìn)行抽象,不同的來源有對應(yīng)不同的實(shí)現(xiàn)類膘流,接口單一職責(zé)絮缅,巧妙精簡的設(shè)計(jì)!類圖如下呼股,AnnotationTransactionAttributeSource是注解相關(guān)耕魄,而NameMatchTransactionAttributeSource、MatchAlwaysTransactionAttributeSource等是XML配置相關(guān)彭谁。

[圖片上傳失敗...(image-57d403-1705114166941)]

TransactionAspectSupport

該抽象父類是事務(wù)切面的基本處理類吸奴,實(shí)現(xiàn)了一些共有方法,如代碼【2】處determineTransactionManager(..)根據(jù)TransactionAttribute得到對應(yīng)的PlatformTransactionManager,以及【3】處createTransactionIfNecessary創(chuàng)建事務(wù)则奥,【4】處completeTransactionAfterThrowing回滾事務(wù)考润,【5】處commitTransactionAfterReturning提交事務(wù)等基本操作,底層同樣是委托PlatformTransactionManager進(jìn)行處理的读处。這里主要看下事務(wù)的回滾操作糊治,跟TransactionTemplate是有區(qū)別的:

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            ...
            if (txInfo.transactionAttribute.rollbackOn(ex)) {【1】
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by rollback error", ex);
                    throw err;
                }
            }
            else {
                try {
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                ...
            }
        }
    }

從【1】處的transactionAttribute.rollbackon(ex)可看出,事務(wù)屬性中的rollbackOn是在這里生效的罚舱,在發(fā)生指定異常時(shí)選擇回滾或提交井辜,是用戶可配置的,而不像TransactionTemplate是固定的全部回滾管闷。

2.TransactionProxyFactoryBean

該類是早期基于Bean的XML配置方式實(shí)現(xiàn)聲明式事務(wù)的核心類粥脚,之所以放在后面講,是因?yàn)樵摲绞揭巡槐煌扑]使用包个,先來看下定義:

/**
 * Proxy factory bean for simplified declarative transaction handling.
 * This is a convenient alternative to a standard AOP
 * {@link org.springframework.aop.framework.ProxyFactoryBean}
 * with a separate {@link TransactionInterceptor} definition.
 *
 * <p><strong>HISTORICAL NOTE:</strong> This class was originally designed to cover the
 * typical case of declarative transaction demarcation: namely, wrapping a singleton
 * target object with a transactional proxy, proxying all the interfaces that the target
 * implements. However, in Spring versions 2.0 and beyond, the functionality provided here
 * is superseded by the more convenient {@code tx:} XML namespace. See the <a
 * >declarative transaction management</a> section of the
 * Spring reference documentation to understand the modern options for managing
 * transactions in Spring applications.
 * ...
 */
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
        implements BeanFactoryAware {
    private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
    private Pointcut pointcut;
    ...
    protected Object createMainInterceptor() {
        this.transactionInterceptor.afterPropertiesSet();
        if (this.pointcut != null) {
            return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
        }
        else {
            // Rely on default pointcut.
            return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
        }
    } 
}

源碼的聲明已經(jīng)相當(dāng)清晰阿逃,大致說明了該類的來龍去脈,忍不住直接貼上來了赃蛛,感興趣可自行閱讀。這里主要是看下其實(shí)現(xiàn)思路:事務(wù)處理邏輯是委托給其成員TransactionInterceptor搀菩,而將事務(wù)邏輯織入目標(biāo)類的工作則交由AbstractSingletonProxyFactoryBean來處理呕臂。FactoryBean是Spring中廣泛使用的用來定制一些較復(fù)雜Bean的實(shí)例化邏輯,因此從類名上就可看出肪跋,AbstractSingletonProxyFactoryBean的主要工作則是實(shí)例化并返回一個(gè)單例的Proxy對象歧蒋。有了Proxy對象,織入的工作就輕而易舉了州既,此時(shí)TransactionInterceptor只是Proxy的眾多Advisor中的一個(gè)谜洽,最后由Proxy創(chuàng)建擁有了事務(wù)增強(qiáng)的代理對象即可。

以下是AbstractSingletonProxyFactoryBean中Proxy的實(shí)例化過程吴叶,全部在afterPropertiesSet中完成阐虚。其中的createMainInterceptor()是在其子類TransactionProxyFactoryBean中實(shí)現(xiàn)的,對應(yīng)事務(wù)增強(qiáng)邏輯蚌卤。

public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
        implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
  ...
  public void afterPropertiesSet() {
        if (this.target == null) {
            throw new IllegalArgumentException("Property 'target' is required");
        }
        if (this.target instanceof String) {
            throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
        }
        if (this.proxyClassLoader == null) {
            this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
        }

        ProxyFactory proxyFactory = new ProxyFactory();【1】

        if (this.preInterceptors != null) {
            for (Object interceptor : this.preInterceptors) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
            }
        }

        // Add the main interceptor (typically an Advisor).
        proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

        if (this.postInterceptors != null) {
            for (Object interceptor : this.postInterceptors) {
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
            }
        }

        proxyFactory.copyFrom(this);

        TargetSource targetSource = createTargetSource(this.target);
        proxyFactory.setTargetSource(targetSource);

        if (this.proxyInterfaces != null) {
            proxyFactory.setInterfaces(this.proxyInterfaces);
        }
        else if (!isProxyTargetClass()) {
            // Rely on AOP infrastructure to tell us what interfaces to proxy.
            proxyFactory.setInterfaces(
                    ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
        }

        this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
    }
}

從代碼【1】處可以看到实束,ProxyFactory是創(chuàng)建Proxy對象的關(guān)鍵類,感興趣的童鞋可以跟進(jìn)ProxyFactory的代碼逊彭,可發(fā)現(xiàn)最終創(chuàng)建Proxy對象的是DefaultAopProxyFactory咸灿,細(xì)節(jié)如下:根據(jù)config配置,選擇創(chuàng)建我們所熟知的兩種AopProxy:JDK的JdkDynamicAopProxy和Cglib的Cglib2AopProxy侮叮。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    ...
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            if (!cglibAvailable) {
                throw new AopConfigException(
                        "Cannot proxy target class because CGLIB2 is not available. " +
                        "Add CGLIB to the class path or specify proxy interfaces.");
            }
            return CglibProxyFactory.createCglibProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
}

需要注意的細(xì)節(jié)

一避矢、PROPAGATION_TESTED(嵌套事務(wù))

當(dāng)使用PROPAGATION_NESTED時(shí),底層的數(shù)據(jù)源必須基于JDBC3.0。因?yàn)镾pring所支持的嵌套事務(wù)审胸,是基于事務(wù)保存點(diǎn)實(shí)現(xiàn)的(JTA除外)亥宿,而保存點(diǎn)機(jī)制是從JDBC3.0才開始出現(xiàn)的。直接看AbstractPlatformTransactionManager中的處理代碼歹嘹。對于通常的嵌套事務(wù)箩绍,會在當(dāng)前所處父事務(wù)中創(chuàng)建保存點(diǎn),然后進(jìn)行子事務(wù)處理尺上;對于JTA事務(wù)環(huán)境材蛛,則是采用嵌套的begin和commit/rollback調(diào)用來處理。

    private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
        ...
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            if (!isNestedTransactionAllowed()) {
                throw new NestedTransactionNotSupportedException(
                        "Transaction manager does not allow nested transactions by default - " +
                        "specify 'nestedTransactionAllowed' property with value 'true'");
            }
            if (debugEnabled) {
                logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
            }
            if (useSavepointForNestedTransaction()) {
                // Create savepoint within existing Spring-managed transaction,
                // through the SavepointManager API implemented by TransactionStatus.
                // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
                DefaultTransactionStatus status =
                        prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                status.createAndHoldSavepoint();
                return status;
            }
            else {
                // Nested transaction through nested begin and commit/rollback calls.
                // Usually only for JTA: Spring synchronization might get activated here
                // in case of a pre-existing JTA transaction.
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, null);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
        }
        ...
    }

二怎抛、獲取數(shù)據(jù)連接資源

當(dāng)脫離模板類卑吭,直接操作底層持久技術(shù)的原生API時(shí),就需要通過Spring提供的資源工具類獲取線程綁定的資源马绝,而不應(yīng)該直接從DataSource或SessionFactory中獲取豆赏,否則容易造成數(shù)據(jù)連接泄露的問題。Spring為不同的持久化技術(shù)提供了一套從TransactionSynchronizationManager中獲取對應(yīng)線程綁定資源的工具類:DataSourceUtils(Spring JDBC或iBatis)富稻、SessionFactoryUtils(Hibernate 3.0)等掷邦。

三、如何標(biāo)注@Transactional注解

雖然@Transactional注解可被應(yīng)用于接口、接口方法、類及類的public方法禀横,但建議在具體實(shí)現(xiàn)類上使用@Transactional注解术徊,因?yàn)?strong>接口上的注解不能被繼承,這樣會有隱患(關(guān)于注解的繼承,可參考這里)。當(dāng)事務(wù)配置按如下方式,使用的是子類代理(CGLib)而非接口代理(JDK)時(shí)胚委,對應(yīng)目標(biāo)類不會添加事務(wù)增強(qiáng)!

<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />

本文同步更新到此處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叉信,一起剝皮案震驚了整個(gè)濱河市亩冬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茉盏,老刑警劉巖鉴未,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸠姨,居然都是意外死亡铜秆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門讶迁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來连茧,“玉大人,你說我怎么就攤上這事⌒パ保” “怎么了客扎?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長罚斗。 經(jīng)常有香客問我徙鱼,道長,這世上最難降的妖魔是什么针姿? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任袱吆,我火速辦了婚禮,結(jié)果婚禮上距淫,老公的妹妹穿的比我還像新娘绞绒。我一直安慰自己,他們只是感情好榕暇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布蓬衡。 她就那樣靜靜地躺著,像睡著了一般彤枢。 火紅的嫁衣襯著肌膚如雪狰晚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天缴啡,我揣著相機(jī)與錄音家肯,去河邊找鬼。 笑死盟猖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的换棚。 我是一名探鬼主播式镐,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼固蚤!你這毒婦竟也來了娘汞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤夕玩,失蹤者是張志新(化名)和其女友劉穎你弦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體燎孟,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禽作,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揩页。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷偿。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萍程,到底是詐尸還是另有隱情幢妄,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布茫负,位于F島的核電站蕉鸳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏忍法。R本人自食惡果不足惜潮尝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缔赠。 院中可真熱鬧衍锚,春花似錦、人聲如沸嗤堰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踢匣。三九已至告匠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間离唬,已是汗流浹背后专。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留输莺,地道東北人戚哎。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像嫂用,于是被迫代替她去往敵國和親型凳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理嘱函,服務(wù)發(fā)現(xiàn)甘畅,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • Spring系列文章 Spring框架-1(基礎(chǔ))Spring框架-2(IOC上)Spring框架-3(IOC下)...
    zhonj閱讀 2,323評論 0 5
  • spring支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式往弓。 編程式事務(wù)管理使用TransactionTemplate...
    熊熊要更努力閱讀 247評論 0 0
  • 事務(wù)簡介 所謂事務(wù)疏唾,指的是程序中可運(yùn)行的不可分割的最小單位。在生活中事務(wù)也是隨處可見的函似。比方說你在Steam上剁手...
    樂百川閱讀 676評論 0 5
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,804評論 6 342