Spring源碼解析——事務增強器

来源:https://www.cnblogs.com/tyson03/archive/2023/10/17/17768862.html
-Advertisement-
Play Games

正文 上一篇文章我們講解了事務的Advisor是如何註冊進Spring容器的,也講解了Spring是如何將有配置事務的類配置上事務的,實際上也就是用了AOP那一套,也講解了Advisor,pointcut驗證流程,至此,事務的初始化工作都已經完成了,在之後的調用過程,如果代理類的方法被調用,都會調用 ...


正文

上一篇文章我們講解了事務的Advisor是如何註冊進Spring容器的,也講解了Spring是如何將有配置事務的類配置上事務的,實際上也就是用了AOP那一套,也講解了Advisor,pointcut驗證流程,至此,事務的初始化工作都已經完成了,在之後的調用過程,如果代理類的方法被調用,都會調用BeanFactoryTransactionAttributeSourceAdvisor這個Advisor的增強方法,也就是我們還未提到的那個Advisor裡面的advise,還記得嗎,在自定義標簽的時候我們將TransactionInterceptor這個Advice作為bean註冊進IOC容器,並且將其註入進Advisor中,這個Advice在代理類的invoke方法中會被封裝到攔截器鏈中,最終事務的功能都在advise中體現,所以我們先來關註一下TransactionInterceptor這個類吧。最全面的Java面試網站

TransactionInterceptor類繼承自MethodInterceptor,所以調用該類是從其invoke方法開始的,首先預覽下這個方法:

@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

重點來了,進入invokeWithinTransaction方法:

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 獲取對應事務屬性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 獲取beanFactory中的transactionManager
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 構造方法唯一標識(類.方法,如:service.UserServiceImpl.save)
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 聲明式事務處理
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 創建TransactionInfo
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // 執行原方法
            // 繼續調用方法攔截器鏈,這裡一般將會調用目標類的方法,如:AccountServiceImpl.save方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // 異常回滾
            completeTransactionAfterThrowing(txInfo, ex);
            // 手動向上拋出異常,則下麵的提交事務不會執行
            // 如果子事務出異常,則外層事務代碼需catch住子事務代碼,不然外層事務也會回滾
            throw ex;
        }
        finally {
            // 消除信息
            cleanupTransactionInfo(txInfo);
        }
        // 提交事務
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        try {
            // 編程式事務處理
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                try {
                    return invocation.proceedWithInvocation();
                }
                catch (Throwable ex) {
                    if (txAttr.rollbackOn(ex)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
            });

            // Check result state: It might indicate a Throwable to rethrow.
            if (throwableHolder.throwable != null) {
                throw throwableHolder.throwable;
            }
            return result;
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }
        catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);
            }
            throw ex2;
        }
        catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }
    }
}

創建事務Info對象

我們先分析事務創建的過程。

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
        @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

    // If no name specified, apply method identification as transaction name.
    // 如果沒有名稱指定則使用方法唯一標識,並使用DelegatingTransactionAttribute封裝txAttr
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 獲取TransactionStatus
            status = tm.getTransaction(txAttr);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured");
            }
        }
    }
    // 根據指定的屬性與status準備一個TransactionInfo
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

對於createTransactionlfNecessary函數主要做了這樣幾件事情。

(1)使用 DelegatingTransactionAttribute 封裝傳入的 TransactionAttribute 實例。

對於傳入的TransactionAttribute類型的參數txAttr,當前的實際類型是RuleBasedTransactionAttribute,是由獲取事務屬性時生成,主要用於數據承載,而這裡之所以使用DelegatingTransactionAttribute進行封裝,當然是提供了更多的功能。

(2)獲取事務。

事務處理當然是以事務為核心,那麼獲取事務就是最重要的事情。

(3)構建事務信息。

根據之前幾個步驟獲取的信息構建Transactionlnfo並返回。

分享一份大彬精心整理的大廠面試手冊,包含電腦基礎、Java基礎、多線程、JVM、資料庫、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分散式、微服務、設計模式、架構、校招社招分享等高頻面試題,非常實用,有小伙伴靠著這份手冊拿過位元組offer~

需要的小伙伴可以自行下載

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

獲取事務

其中核心是在getTransaction方法中:

@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // 獲取一個transaction
    Object transaction = doGetTransaction();

    boolean debugEnabled = logger.isDebugEnabled();

    if (definition == null) {
        definition = new DefaultTransactionDefinition();
    }
    // 如果在這之前已經存在事務了,就進入存在事務的方法中
    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    
    // 事務超時設置驗證
    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }

    // 走到這裡說明此時沒有存在事務,如果傳播特性是MANDATORY時拋出異常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
                "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    // 如果此時不存在事務,當傳播特性是REQUIRED或NEW或NESTED都會進入if語句塊
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        // PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED都需要新建事務
        // 因為此時不存在事務,將null掛起
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            // new一個status,存放剛剛創建的transaction,然後將其標記為新事務!
            // 這裡transaction後面一個參數決定是否是新事務!
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 新開一個連接的地方,非常重要
            doBegin(transaction, definition);
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                    "isolation level will effectively be ignored: " + definition);
        }
        // 其他的傳播特性一律返回一個空事務,transaction = null
        //當前不存在事務,且傳播機制=PROPAGATION_SUPPORTS/PROPAGATION_NOT_SUPPORTED/PROPAGATION_NEVER,這三種情況,創建“空”事務
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
}

先來看看transaction是如何被創建出來的:

@Override
protected Object doGetTransaction() {
    // 這裡DataSourceTransactionObject是事務管理器的一個內部類
    // DataSourceTransactionObject就是一個transaction,這裡new了一個出來
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    // 解綁與綁定的作用在此時體現,如果當前線程有綁定的話,將會取出holder
    // 第一次conHolder肯定是null
    ConnectionHolder conHolder =
    (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
    // 此時的holder被標記成一個舊holder
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
}

創建transaction過程很簡單,接著就會判斷當前是否存在事務:

@Override
protected boolean isExistingTransaction(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

public boolean hasConnectionHolder() {
    return (this.connectionHolder != null);
}

這裡判斷是否存在事務的依據主要是獲取holder中的transactionActive變數是否為true,如果是第一次進入事務,holder直接為null判斷不存在了,如果是第二次進入事務transactionActive變數是為true的(後面會提到是在哪裡把它變成true的),由此來判斷當前是否已經存在事務了。

至此,源碼分成了2條處理線:

1.當前已存在事務:isExistingTransaction()判斷是否存在事務,存在事務handleExistingTransaction()根據不同傳播機制不同處理

2.當前不存在事務: 不同傳播機制不同處理

當前不存在事務

如果不存在事務,傳播特性又是REQUIRED或NEW或NESTED,將會先掛起null,這個掛起方法我們後面再講,然後創建一個DefaultTransactionStatus ,並將其標記為新事務,然後執行doBegin(transaction, definition);這個方法也是一個關鍵方法

神秘又關鍵的status對象

TransactionStatus介面

public interface TransactionStatus extends SavepointManager, Flushable {
    // 返回當前事務是否為新事務(否則將參與到現有事務中,或者可能一開始就不在實際事務中運行)
    boolean isNewTransaction();
    // 返回該事務是否在內部攜帶保存點,也就是說,已經創建為基於保存點的嵌套事務。
    boolean hasSavepoint();
    // 設置事務僅回滾。
    void setRollbackOnly();
    // 返回事務是否已標記為僅回滾
    boolean isRollbackOnly();
    // 將會話刷新到數據存儲區
    @Override
    void flush();
    // 返回事物是否已經完成,無論提交或者回滾。
    boolean isCompleted();
}

再來看看實現類DefaultTransactionStatus

DefaultTransactionStatus

public class DefaultTransactionStatus extends AbstractTransactionStatus {

    //事務對象
    @Nullable
    private final Object transaction;

    //事務對象
    private final boolean newTransaction;

    private final boolean newSynchronization;

    private final boolean readOnly;

    private final boolean debug;

    //事務對象
    @Nullable
    private final Object suspendedResources;
    
        public DefaultTransactionStatus(
            @Nullable Object transaction, boolean newTransaction, boolean newSynchronization,
            boolean readOnly, boolean debug, @Nullable Object suspendedResources) {

        this.transaction = transaction;
        this.newTransaction = newTransaction;
        this.newSynchronization = newSynchronization;
        this.readOnly = readOnly;
        this.debug = debug;
        this.suspendedResources = suspendedResources;
    }
    
    //略...
}

我們看看這行代碼 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

// 這裡是構造一個status對象的方法
protected DefaultTransactionStatus newTransactionStatus(
    TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
    boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

    boolean actualNewSynchronization = newSynchronization &&
        !TransactionSynchronizationManager.isSynchronizationActive();
    return new DefaultTransactionStatus(
        transaction, newTransaction, actualNewSynchronization,
        definition.isReadOnly(), debug, suspendedResources);
}

實際上就是封裝了事務屬性definition,新創建的transaction,並且將事務狀態屬性設置為新事務,最後一個參數為被掛起的事務。

簡單瞭解一下關鍵參數即可:

第二個參數transaction:事務對象,在一開頭就有創建,其就是事務管理器的一個內部類。

第三個參數newTransaction:布爾值,一個標識,用於判斷是否是新的事務,用於提交或者回滾方法中,是新的才會提交或者回滾。

最後一個參數suspendedResources:被掛起的對象資源,掛起操作會返回舊的holder,將其與一些事務屬性一起封裝成一個對象,就是這個suspendedResources這個對象了,它會放在status中,在最後的清理工作方法中判斷status中是否有這個掛起對象,如果有會恢復它

接著我們來看看關鍵代碼 doBegin(transaction, definition);

 @Override
 protected void doBegin(Object transaction, TransactionDefinition definition) {
     DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
     Connection con = null;
 
     try {
         // 判斷如果transaction沒有holder的話,才去從dataSource中獲取一個新連接
         if (!txObject.hasConnectionHolder() ||
                 txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
             //通過dataSource獲取連接
             Connection newCon = this.dataSource.getConnection();
             if (logger.isDebugEnabled()) {
                 logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
             }
             // 所以,只有transaction中的holder為空時,才會設置為新holder
             // 將獲取的連接封裝進ConnectionHolder,然後封裝進transaction的connectionHolder屬性
             txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
         }
      //設置新的連接為事務同步中
         txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
         con = txObject.getConnectionHolder().getConnection();
      //conn設置事務隔離級別,只讀
         Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
         txObject.setPreviousIsolationLevel(previousIsolationLevel);//DataSourceTransactionObject設置事務隔離級別
 
         // 如果是自動提交切換到手動提交
         if (con.getAutoCommit()) {
             txObject.setMustRestoreAutoCommit(true);
             if (logger.isDebugEnabled()) {
                 logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
             }
             con.setAutoCommit(false);
         }
      // 如果只讀,執行sql設置事務只讀
         prepareTransactionalConnection(con, definition);
         // 設置connection持有者的事務開啟狀態
         txObject.getConnectionHolder().setTransactionActive(true);
 
         int timeout = determineTimeout(definition);
         if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
             // 設置超時秒數
             txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
         }
 
         // 將當前獲取到的連接綁定到當前線程
         if (txObject.isNewConnectionHolder()) {
             TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
         }
     }catch (Throwable ex) {
         if (txObject.isNewConnectionHolder()) {
             DataSourceUtils.releaseConnection(con, this.dataSource);
             txObject.setConnectionHolder(null, false);
         }
         throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
     }
 }

conn設置事務隔離級別

@Nullable
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
        throws SQLException {

    Assert.notNull(con, "No Connection specified");

    // Set read-only flag.
    // 設置數據連接的只讀標識
    if (definition != null && definition.isReadOnly()) {
        try {
            if (logger.isDebugEnabled()) {
                logger.debug("Setting JDBC Connection [" + con + "] read-only");
            }
            con.setReadOnly(true);
        }
        catch (SQLException | RuntimeException ex) {
            Throwable exToCheck = ex;
            while (exToCheck != null) {
                if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
                    // Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0
                    throw ex;
                }
                exToCheck = exToCheck.getCause();
            }
            // "read-only not supported" SQLException -> ignore, it's just a hint anyway
            logger.debug("Could not set JDBC Connection read-only", ex);
        }
    }

    // Apply specific isolation level, if any.
    // 設置資料庫連接的隔離級別
    Integer previousIsolationLevel = null;
    if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
        if (logger.isDebugEnabled()) {
            logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
                    definition.getIsolationLevel());
        }
        int currentIsolation = con.getTransactionIsolation();
        if (currentIsolation != definition.getIsolationLevel()) {
            previousIsolationLevel = currentIsolation;
            con.setTransactionIsolation(definition.getIsolationLevel());
        }
    }

    return previousIsolationLevel;
}

我們看到都是通過 Connection 去設置

線程變數的綁定

我們看 doBegin 方法的47行,將當前獲取到的連接綁定到當前線程,綁定與解綁圍繞一個線程變數,此變數在TransactionSynchronizationManager類中:

private static final ThreadLocal<Map<Object, Object>> resources =  new NamedThreadLocal<>("Transactional resources");

這是一個 static final 修飾的 線程變數,存儲的是一個Map,我們來看看47行的靜態方法,bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {
    // 從上面可知,線程變數是一個Map,而這個Key就是dataSource
    // 這個value就是holder
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Assert.notNull(value, "Value must not be null");
    // 獲取這個線程變數Map
    Map<Object, Object> map = resources.get();
    // set ThreadLocal Map if none found
    if (map == null) {
        map = new HashMap<>();
        resources.set(map);
    }
    // 將新的holder作為value,dataSource作為key放入當前線程Map中
    Object oldValue = map.put(actualKey, value);
    // Transparently suppress a ResourceHolder that was marked as void...
    if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
        oldValue = null;
    }
    if (oldValue != null) {
        throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
                actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
    }    Thread.currentThread().getName() + "]");
    }
    // 略...
}

擴充知識點

這裡再擴充一點,mybatis中獲取的資料庫連接,就是根據 dataSource 從ThreadLocal中獲取的

以查詢舉例,會調用Executor#doQuery方法:

最終會調用DataSourceUtils#doGetConnection獲取,真正的資料庫連接,其中TransactionSynchronizationManager中保存的就是方法調用前,spring增強方法中綁定到線程的connection,從而保證整個事務過程中connection的一致性

我們看看TransactionSynchronizationManager.getResource(Object key)這個方法

@Nullable
public static Object getResource(Object key) {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Object value = doGetResource(actualKey);
    if (value != null && logger.isTraceEnabled()) {
        logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                Thread.currentThread().getName() + "]");
    }
    return value;
}

    @Nullable
private static Object doGetResource(Object actualKey) {
    Map<Object, Object> map = resources.get();
    if (map == null) {
        return null;
    }
    Object value = map.get(actualKey);
    // Transparently remove ResourceHolder that was marked as void...
    if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
        map.remove(actualKey);
        // Remove entire ThreadLocal if empty...
        if (map.isEmpty()) {
            resources.remove();
        }
        value = null;
    }
    return value;
}

就是從線程變數的Map中根據 DataSource獲取 ConnectionHolder

已經存在的事務

前面已經提到,第一次事務開始時必會新創一個holder然後做綁定操作,此時線程變數是有holder的且avtive為true,如果第二個事務進來,去new一個transaction之後去線程變數中取holder,holder是不為空的且active是為true的,所以會進入handleExistingTransaction方法:

 private TransactionStatus handleExistingTransaction(
         TransactionDefinition definition, Object transaction, boolean debugEnabled)
         throws TransactionException {
    // 1.NERVER(不支持當前事務;如果當前事務存在,拋出異常)報錯
     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
         throw new IllegalTransactionStateException(
                 "Existing transaction found for transaction marked with propagation 'never'");
     }
    // 2.NOT_SUPPORTED(不支持當前事務,現有同步將被掛起)掛起當前事務,返回一個空事務
     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
         if (debugEnabled) {
             logger.debug("Suspending current transaction");
         }
         // 這裡會將原來的事務掛起,並返回被掛起的對象
         Object suspendedResources = suspend(transaction);
         boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
         // 這裡可以看到,第二個參數transaction傳了一個空事務,第三個參數false為舊標記
         // 最後一個參數就是將前面掛起的對象封裝進新的Status中,當前事務執行完後,就恢復suspendedResources
         return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);
     }
    // 3.REQUIRES_NEW掛起當前事務,創建新事務
     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
         if (debugEnabled) {
             logger.debug("Suspending current transaction, creating new transaction with name [" +
                     definition.getName() + "]");
         }
         // 將原事務掛起,此時新建事務,不與原事務有關係
         // 會將transaction中的holder設置為null,然後解綁!
         SuspendedResourcesHolder suspendedResources = suspend(transaction);
         try {
             boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
             // new一個status出來,傳入transaction,並且為新事務標記,然後傳入掛起事務
             DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
             // 這裡也做了一次doBegin,此時的transaction中holer是為空的,因為之前的事務被掛起了
             // 所以這裡會取一次新的連接,並且綁定!
             doBegin(transaction, definition);
             prepareSynchronization(status, definition);
             return status;
         }
         catch (RuntimeException beginEx) {
             resumeAfterBeginException(transaction, suspendedResources, beginEx);
             throw beginEx;
         }
         catch (Error beginErr) {
             resumeAfterBeginException(transaction, suspendedResources, beginErr);
             throw beginErr;
         }
     }
   // 如果此時的傳播特性是NESTED,不會掛起事務
     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() + "]");
         }
         // 這裡如果是JTA事務管理器,就不可以用savePoint了,將不會進入此方法
         if (useSavepointForNestedTransaction()) { 
             // 這裡不會掛起事務,說明NESTED的特性是原事務的子事務而已
             // new一個status,傳入transaction,傳入舊事務標記,傳入掛起對象=null
             DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
             // 這裡是NESTED特性特殊的地方,在先前存在事務的情況下會建立一個savePoint
             status.createAndHoldSavepoint();
             return status;
         }
         else {
             // JTA事務走這個分支,創建新事務
             boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
             DefaultTransactionStatus status = newTransactionStatus(
                     definition, transaction, true, newSynchronization, debugEnabled, null);
             doBegin(transaction, definition);
             prepareSynchronization(status, definition);
             return status;
         }
     }
 
     // 到這裡PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED或PROPAGATION_MANDATORY,存在事務加入事務即可,標記為舊事務,空掛起
     boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
     return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
 }

對於已經存在事務的處理過程中,我們看到了很多熟悉的操作,但是,也有些不同的地方,函數中對已經存在的事務處理考慮兩種情況。

(1)PROPAGATION_REQUIRES_NEW表示當前方法必須在它自己的事務里運行,一個新的事務將被啟動,而如果有一個事務正在運行的話,則在這個方法運行期間被掛起。而Spring中對於此種傳播方式的處理與新事務建立最大的不同點在於使用suspend方法將原事務掛起。 將信息掛起的目的當然是為了在當前事務執行完畢後在將原事務還原。

(2)PROPAGATION_NESTED表示如果當前正有一個事務在運行中,則該方法應該運行在一個嵌套的事務中,被嵌套的事務可以獨立於封裝事務進行提交或者回滾,如果封裝事務不存在,行為就像PROPAGATION_REQUIRES_NEW。對於嵌入式事務的處理,Spring中主要考慮了兩種方式的處理。

  • Spring中允許嵌入事務的時候,則首選設置保存點的方式作為異常處理的回滾。
  • 對於其他方式,比如JTA無法使用保存點的方式,那麼處理方式與PROPAGATION_ REQUIRES_NEW相同,而一旦出現異常,則由Spring的事務異常處理機制去完成後續操作。

對於掛起操作的主要目的是記錄原有事務的狀態,以便於後續操作對事務的恢復

小結

到這裡我們可以知道,在當前存在事務的情況下,根據傳播特性去決定是否為新事務,是否掛起當前事務。

NOT_SUPPORTED :會掛起事務,不運行doBegin方法傳空transaction,標記為舊事務。封裝status對象:

return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources)

REQUIRES_NEW :將會掛起事務且運行doBegin方法,標記為新事務。封裝status對象:

DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

NESTED :不會掛起事務且不會運行doBegin方法,標記為舊事務,但會創建savePoint。封裝status對象:

DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);

其他事務例如REQUIRED :不會掛起事務,封裝原有的transaction不會運行doBegin方法,標記舊事務,封裝status對象:

return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

掛起

對於掛起操作的主要目的是記錄原有事務的狀態,以便於後續操作對事務的恢復:

@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
        try {
            Object suspendedResources = null;
            if (transaction != null) {
                // 這裡是真正做掛起的方法,這裡返回的是一個holder
                suspendedResources = doSuspend(transaction);
            }
            // 這裡將名稱、隔離級別等信息從線程變數中取出並設置對應屬性為null到線程變數
            String name = TransactionSynchronizationManager.getCurrentTransactionName();
            TransactionSynchronizationManager.setCurrentTransactionName(null);
            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
            Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
            boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
            TransactionSynchronizationManager.setActualTransactionActive(false);
            // 將事務各個屬性與掛起的holder一併封裝進SuspendedResourcesHolder對象中
            return new SuspendedResourcesHolder(
                suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
        }
        catch (RuntimeException | Error ex) {
            // doSuspend failed - original transaction is still active...
            doResumeSynchronization(suspendedSynchronizations);
            throw ex;
        }
    }
    else if (transaction != null) {
        // Transaction active but no synchronization active.
        Object suspendedResources = doSuspend(transaction);
        return new SuspendedResourcesHolder(suspendedResources);
    }
    else {
        // Neither transaction nor synchronization active.
        return null;
    }
}
@Override
protected Object doSuspend(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    // 將transaction中的holder屬性設置為空
    txObject.setConnectionHolder(null);
    // ConnnectionHolder從線程變數中解綁!
    return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}

我們來看看 unbindResource

private static Object doUnbindResource(Object actualKey) {
    // 取得當前線程的線程變數Map
    Map<Object, Object> map = resources.get();
    if (map == null) {
        return null;
    }
    // 將key為dataSourece的value移除出Map,然後將舊的Holder返回
    Object value = map.remove(actualKey);
    // Remove entire ThreadLocal if empty...
    // 如果此時map為空,直接清除線程變數
    if (map.isEmpty()) {
        resources.remove();
    }
    // Transparently suppress a ResourceHolder that was marked as void...
    if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
        value = null;
    }
    if (value != null && logger.isTraceEnabled()) {
        logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +
                     Thread.currentThread().getName() + "]");
    }
    // 將舊Holder返回
    return value;
}

可以回頭看一下解綁操作的介紹。這裡掛起主要幹了三件事:

  1. 將transaction中的holder屬性設置為空
  2. 解綁(會返回線程中的那個舊的holder出來,從而封裝到SuspendedResourcesHolder對象中)
  3. 將SuspendedResourcesHolder放入status中,方便後期子事務完成後,恢復外層事務

最後給大家分享一個Github倉庫,上面有大彬整理的300多本經典的電腦書籍PDF,包括C語言、C++、Java、Python、前端、資料庫、操作系統、電腦網路、數據結構和演算法、機器學習、編程人生等,可以star一下,下次找書直接在上面搜索,倉庫持續更新中~

Github地址

如果訪問不了Github,可以訪問碼雲地址。

碼雲地址


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 我打算寫一個系列,內容是將python註入到其他進程實現inline hook和主動調用。本篇文章是這個系列的第一篇,後面用到的案例是註入python到PC微信實現基本的收發消息。文章著重於python方面的內容,所以對於微信找收發消息的call不會去講過程,有興趣的可以直接百度搜PC微信逆向 ...
  • 1 2FA 的定義 雙因素身份驗證 (2FA) 是一種身份和訪管理安全方法,需要經過兩種形式的身份驗證才能訪河資源和數據,2FA使企業能夠監視和幫助保護其最易受攻擊的信息和網路。 2 2FA 的身份驗證方法 使用雙因素身份驗證時有不同的身份驗證方法。此處列出了一些最受歡迎的選項。 2.1 硬體令牌 ...
  • package chap03; import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultS ...
  • 一、審核功能實現的方式 1、普通 方案:經辦時入A表,審核後從A表讀取數據,然後操作目標B表; 優勢:思路簡單 劣勢:對後端功能實行高度的嵌入;審核功能數據操作不統一 2、彈框式 方案:前臺實現,操作時判斷是否需要許可權控制,如果需要,則彈出框,由審核人員進行審核,審核通過後,進行後續操作。 優勢:對 ...
  • C語音-數據類型 數據類型 中文名稱 空間大小(bite - 位元組) char 字元串數據類 1 short (int) 短整型 2 int 整形 4 long 長整形 4 long long 更長的整形 8 float 單精度浮點數 4 double 雙精度浮點數 8 include <> int ...
  • 最近這段時間收到了一些讀者的私信,問我某個技術要不要學,還有一些在國外的同學竟然對 Java 圖形化很感興趣,還想找這方面的工作。 比較忙,一直沒抽出時間去回答這類問題,剛好看到我關註的一位大佬回答過,這裡分享一下,希望對你能有幫助。 下麵是正文。 原文鏈接:https://www.zhihu.co ...
  • 網路上的文件傳輸功能也是很有必要實現一下的,網路傳輸文件的過程通常分為客戶端和伺服器端兩部分。客戶端可以選擇上傳或下載文件,將文件分塊並逐塊發送到伺服器,或者從伺服器分塊地接收文件。伺服器端接收來自客戶端的請求,根據請求類型執行對應的操作,並根據發送的文件名或其他標識來確定要傳輸的文件。在實現文件傳... ...
  • 0 概述 通常聲明一個數組時需要使用一個常量來指定數組的長度,數組所占用的記憶體是在編譯時就被分配。這種方式的聲明的優點是簡單,但是存在以下幾個缺點: 使用的元素數量超過數組聲明的長度,當前數組就不能存儲相應的數據; 如果數組的長度被聲明很大,實際使用的元素又比較少會導致記憶體空間的浪費; 程式開發中會 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...