首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 聲明式事務管理源碼深度解讀(一):事務開始與屬性解析全流程分析

     編輯:Java 相關知識

核心問題:Spring 如何判定方法需要事務?

聲明式事務的起點是:針對特定方法,解析出 TransactionAttribute(傳播行為、隔離級別、超時、只讀、回滾規則),決定是否創建/加入事務。這一過程集中在 TransactionAspectSupport 及其子類 TransactionInterceptor 中。

TransactionDefinition / TransactionAttribute:事務元數據契約

常量類別關鍵常量含義
傳播行為PROPAGATION_REQUIRED (0)支持現有事務,無則新建(默認)
PROPAGATION_SUPPORTS支持現有事務,無則非事務運行
PROPAGATION_MANDATORY必須在現有事務中運行,無則拋異常
PROPAGATION_REQUIRES_NEW掛起現有事務,強制新建
PROPAGATION_NOT_SUPPORTED掛起現有事務,非事務運行
PROPAGATION_NEVER不得在事務中運行,否則拋異常
PROPAGATION_NESTED嵌套事務(保存點),無則同 REQUIRED
隔離級別ISOLATION_DEFAULT (-1)使用底層數據庫默認
ISOLATION_READ_UNCOMMITTED讀未提交
ISOLATION_READ_COMMITTED讀已提交(防髒讀)
ISOLATION_REPEATABLE_READ可重複讀(防不可重複讀)
ISOLATION_SERIALIZABLE串行化(防幻讀)

TransactionAttribute 繼承 TransactionDefinition,額外提供 rollbackOn(Throwable) 判斷異常是否觸發回滾。

屬性解析入口:TransactionAttributeSource

統一接口:

public interface TransactionAttributeSource {
    TransactionAttribute getTransactionAttribute(Method method, Class targetClass);
}

核心實現 AbstractFallbackTransactionAttributeSource:

  • 緩存優先:Key = method + targetClass,Value = TransactionAttribute 或 NULL_TRANSACTION_ATTRIBUTE 哨兵。
  • 計算邏輯:computeTransactionAttribute() 多級回退(方法 → 類 → 接口方法 → 接口類)。
  • 兩大子類:
    1. AnnotationTransactionAttributeSource:掃描 @Transactional 註解(類/方法/接口)。
    2. NameMatchTransactionAttributeSource:XML 配置 <tx:method name="*" propagation="REQUIRED"/>。

切點匹配:TransactionAttributeSourceAdvisor

public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();
    
    private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut {
        public boolean matches(Method method, Class targetClass) {
            TransactionAttributeSource tas = getTransactionAttributeSource();
            return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);
        }
    }
}

邏輯極簡:**能解析出屬性即匹配**,將「是否事務化」委託給屬性源解析器。

事務創建核心:createTransactionIfNecessary()

protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) {
    // 1. 解析事務屬性
    final TransactionAttribute sourceAttr = transactionAttributeSource.getTransactionAttribute(method, targetClass);
    TransactionAttribute txAttr = sourceAttr;
    
    // 2. 補全事務名稱(方法簽名)
    if (txAttr != null && txAttr.getName() == null) {
        final String name = methodIdentification(method);
        txAttr = new DelegatingTransactionAttribute(sourceAttr) {
            public String getName() { return name; }
        };
    }
    
    // 3. 創建 TransactionInfo 記錄上下文
    TransactionInfo txInfo = new TransactionInfo(txAttr, method);
    
    // 4. 若需要事務,從事務管理器獲取 TransactionStatus
    if (txAttr != null) {
        if (logger.isDebugEnabled()) logger.debug("Getting transaction for " + txInfo.joinpointIdentification());
        txInfo.newTransactionStatus(transactionManager.getTransaction(txAttr));
    } else {
        if (logger.isDebugEnabled()) logger.debug("Don't need transaction for " + methodIdentification(method));
    }
    
    // 5. 綁定到線程(棧機制)
    txInfo.bindToThread();
    return txInfo;
}

TransactionInfo:輕量級上下文持有者

protected static class TransactionInfo {
    private final TransactionAttribute transactionAttribute;
    private final String joinpointIdentification;
    private TransactionStatus transactionStatus;
    private TransactionInfo oldTransactionInfo; // 棧指針
    
    public void newTransactionStatus(TransactionStatus status) { this.transactionStatus = status; }
    public TransactionStatus getTransactionStatus() { return transactionStatus; }
    public boolean hasTransaction() { return transactionStatus != null; }
    
    private void bindToThread() {
        oldTransactionInfo = (TransactionInfo) currentTransactionInfo.get();
        currentTransactionInfo.set(this);
    }
}

關鍵設計:TransactionInfo 不持有事務實體,只持有 TransactionStatus(事務狀態句柄),實現上下文與事務實體分離。

PlatformTransactionManager.getTransaction():事務獲取模板

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    Object transaction = doGetTransaction(); // 子類獲取底層事務對象(Connection/Session)
    boolean debugEnabled = logger.isDebugEnabled();
    if (definition == null) definition = new DefaultTransactionDefinition();
    
    if (isExistingTransaction(transaction)) { // 判斷是否已有事務
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    
    // 無現有事務:根據傳播行為決策
    if (definition.getTimeout() < TIMEOUT_DEFAULT) throw new InvalidTimeoutException(...);
    
    if (definition.getPropagationBehavior() == PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException("No existing transaction for MANDATORY");
    } else if (definition.getPropagationBehavior() == PROPAGATION_REQUIRED ||
               definition.getPropagationBehavior() == PROPAGATION_REQUIRES_NEW ||
               definition.getPropagationBehavior() == PROPAGATION_NESTED) {
        if (debugEnabled) logger.debug("Creating new transaction with name [" + definition.getName() + "]");
        doBegin(transaction, definition); // 子類實現:開啟事務
        boolean newSync = (transactionSynchronization != SYNCHRONIZATION_NEVER);
        return newTransactionStatus(definition, transaction, true, newSync, debugEnabled, null); // isNewTransaction = true
    } else {
        // SUPPORTS/NOT_SUPPORTED/NEVER:不創建事務
        boolean newSync = (transactionSynchronization == SYNCHRONIZATION_ALWAYS);
        return newTransactionStatus(definition, null, false, newSync, debugEnabled, null); // isNewTransaction = false
    }
}

HibernateTransactionManager.doBegin():具體事務啟動實現

protected void doBegin(Object transaction, TransactionDefinition definition) {
    HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
    
    // 1. 獲取/創建 Session
    if (txObject.getSessionHolder() == null) {
        Session newSession = (entityInterceptor != null) 
            ? sessionFactory.openSession(entityInterceptor) 
            : sessionFactory.openSession();
        txObject.setSessionHolder(new SessionHolder(newSession), true); // newSessionHolder = true
    }
    
    Session session = txObject.getSessionHolder().getSession();
    txObject.getSessionHolder().setSynchronizedWithTransaction(true);
    
    // 2. JDBC Connection 設置隔離級別、只讀
    Connection con = session.connection();
    Integer previousIsolation = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    txObject.setPreviousIsolationLevel(previousIsolation);
    
    if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
        session.setFlushMode(FlushMode.NEVER); // 只讀優化
    }
    
    // 3. 開啟 Hibernate 事務
    txObject.getSessionHolder().setTransaction(session.beginTransaction());
    
    // 4. 綁定資源到線程(事務同步)
    if (txObject.isNewSessionHolder()) {
        TransactionSynchronizationManager.bindResource(sessionFactory, txObject.getSessionHolder());
    }
    if (dataSource != null) {
        ConnectionHolder conHolder = new ConnectionHolder(con);
        TransactionSynchronizationManager.bindResource(dataSource, conHolder);
        txObject.setConnectionHolder(conHolder);
    }
    
    // 5. 超時設置
    if (definition.getTimeout() != TIMEOUT_DEFAULT) {
        txObject.getSessionHolder().setTimeoutInSeconds(definition.getTimeout());
    }
}

關鍵點:Session/Connection 的線程綁定

  • TransactionSynchronizationManager.bindResource(key, holder) 將 SessionHolder/ConnectionHolder 存入 ThreadLocal>。
  • 同一線程後續 DAO 操作通過 SessionFactoryUtils.doGetSession() 獲取同一 Session,實現 事務傳播。
  • 若無事務上下文且 allowCreate=false,拋出 HibernateException(「No Session found for current thread」)。

SessionFactoryUtils.doGetSession():事務感知 Session 獲取

public static Session doGetSession(SessionFactory sessionFactory, boolean allowCreate) {
    SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    if (holder != null && holder.hasSession()) {
        holder.requested();
        return holder.getSession();
    }
    if (allowCreate) {
        return sessionFactory.openSession(); // 非事務模式創建新 Session
    }
    throw new IllegalStateException("No Session bound to thread, and allowCreate=false");
}

總結:事務開始的完整鏈路

方法調用進入 TransactionInterceptor.invoke()
    ↓
createTransactionIfNecessary(method, targetClass)
    ├─ transactionAttributeSource.getTransactionAttribute() → TransactionAttribute (緩存/計算)
    ├─ new TransactionInfo(txAttr)
    ├─ txAttr != null?
    │   ├─ 是 → transactionManager.getTransaction(txAttr)
    │   │       ├─ doGetTransaction() → HibernateTransactionObject
    │   │       ├─ isExistingTransaction()? → handleExistingTransaction() (傳播行為決策)
    │   │       └─ 無現有事務 + REQUIRED/REQUIRES_NEW/NESTED → doBegin()
    │   │             ├─ 獲取/創建 Session
    │   │             ├─ 設置隔離級別、只讀
    │   │             ├─ session.beginTransaction() → 真實事務開始
    │   │             ├─ 綁定 SessionHolder/ConnectionHolder 到 ThreadLocal
    │   │             └─ newTransactionStatus(..., true, ...) → isNewTransaction = true
    │   └─ txInfo.newTransactionStatus(status)
    └─ 否 → txInfo 無 TransactionStatus
    ↓
txInfo.bindToThread() → 推入 TransactionInfo 棧
    ↓
返回 txInfo,繼續 invocation.proceed() 執行業務邏輯

核心洞見

  1. 屬性解析與切點匹配合二為一:TransactionAttributeSource 同時承擔「解析屬性」與「判定是否事務化」雙重職責。
  2. TransactionInfo 棧管理上下文可見性:bindToThread()/cleanup() 實現巢狀事務上下文的正確進出棧。
  3. isNewTransaction() 決定真實提交權:只有最外層 REQUIRED 事務擁有 true,內層共用事務為 false。
  4. 資源綁定到 ThreadLocal 實現事務傳播:Session/Connection 在線程局部變量中傳遞,無需參數傳遞。
  5. 模板方法模式貫穿始終:AbstractPlatformTransactionManager 定義骨架,具體管理器填充 doGetTransaction()、isExistingTransaction()、doBegin()、doCommit()、doRollback()。

掌握這條鏈路,是理解 Spring 事務傳播行為、排查「事務不生效」、「Session 不共享」、「嵌套事務回滾異常」的基石。下一篇將深入分析事務提交/回滾的完整邊界控制流程。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved