聲明式事務的起點是:針對特定方法,解析出 TransactionAttribute(傳播行為、隔離級別、超時、只讀、回滾規則),決定是否創建/加入事務。這一過程集中在 TransactionAspectSupport 及其子類 TransactionInterceptor 中。
| 常量類別 | 關鍵常量 | 含義 |
|---|---|---|
| 傳播行為 | 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) 判斷異常是否觸發回滾。
統一接口:
public interface TransactionAttributeSource {
TransactionAttribute getTransactionAttribute(Method method, Class> targetClass);
}
核心實現 AbstractFallbackTransactionAttributeSource:
NULL_TRANSACTION_ATTRIBUTE 哨兵。computeTransactionAttribute() 多級回退(方法 → 類 → 接口方法 → 接口類)。AnnotationTransactionAttributeSource:掃描 @Transactional 註解(類/方法/接口)。NameMatchTransactionAttributeSource:XML 配置 <tx:method name="*" propagation="REQUIRED"/>。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);
}
}
}
邏輯極簡:**能解析出屬性即匹配**,將「是否事務化」委託給屬性源解析器。
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;
}
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(事務狀態句柄),實現上下文與事務實體分離。
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
}
}
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());
}
}
TransactionSynchronizationManager.bindResource(key, holder) 將 SessionHolder/ConnectionHolder 存入 ThreadLocal。SessionFactoryUtils.doGetSession() 獲取同一 Session,實現 事務傳播。allowCreate=false,拋出 HibernateException(「No Session found for current thread」)。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() 執行業務邏輯
TransactionAttributeSource 同時承擔「解析屬性」與「判定是否事務化」雙重職責。bindToThread()/cleanup() 實現巢狀事務上下文的正確進出棧。true,內層共用事務為 false。AbstractPlatformTransactionManager 定義骨架,具體管理器填充 doGetTransaction()、isExistingTransaction()、doBegin()、doCommit()、doRollback()。掌握這條鏈路,是理解 Spring 事務傳播行為、排查「事務不生效」、「Session 不共享」、「嵌套事務回滾異常」的基石。下一篇將深入分析事務提交/回滾的完整邊界控制流程。