本文由 簡悅 SimpRead 轉碼, 原文地址 www.kailing.pub 引言
寫這篇博文有個來由,是為了解決博主遇到的多數據源的事務問題(用不了 JTA),所以深入到 spring-tx 的源碼去學習了一番,非常有收獲,最后博主的分布式事務問題也迎刃而解了,這個文章算個開篇,關於如何處理多數據源事務,待下文分解。本文涉及到的技術包含 spring aop 的使用、spring bean 生命周期等,如果能夠真正理解 Transactional 的工作原理,對排查事務相關的問題有非常大的幫助。
spring-tx 版本:5.0.2
工作機制簡述
先來看一張官方的事務簡圖:
spring 定義了 @Transactional 注解,基於 AbstractBeanFactoryPointcutAdvisor、StaticMethodMatcherPointcut、MethodInterceptor 的 aop 編程模式,增強了添加 @Transactional 注解的方法。同時抽象了事務行為為 PlatformTransactionManager(事務管理器)、TransactionStatus(事務狀態)、TransactionDefinition(事務定義) 等形態。最終將事務的開啟、提交、回滾等邏輯嵌入到被增強的方法的前后,完成統一的事務模型管理。
事務 aop 核心類釋義
@Transactional
事務注解,用於定位 aop 的切入點,事務注解里包含了完整事務的所有基本屬性,常見的屬性如:
- transactionManager:事務管理器
- propagation:傳播行為定義,枚舉類型,是 spring 獨有的事務行為設計,默認為 PROPAGATION_REQUIRED(支持當前事務,不存在則新建)
- isolation:隔離級別,對應數據庫的隔離級別實現,mysql 默認的隔離級別是 read-committed
- timeout:超時時間,默認使用數據庫的超時,mysql 默認的事務等待超時為 5 分鍾
- readOnly:是否只讀,默認是 false
- rollbackFor:異常回滾列表,默認的是 RuntimeException 異常回滾
TransactionAttribute
事務屬性抽象接口類,承載了 @Transactional 注解里的所有屬性,實現類的繼承關系如下類結構圖,這個實例在被注解解析器創建好后,會在事務上下文中傳遞
SpringTransactionAnnotationParser
見名知意,這個類是 spring 的事務注解解析器,實現自 TransactionAnnotationParser 接口,是 spring 管理的事務解析器,用於解析 @Transactional 注解,將注解里的屬性設置到 TransactionAttribute 的實現類屬性里。除了這個,另還有兩個實現,分別是 JTA 事務注解解析器,和 EJB 事務注解管理解析器,區別是解析的注解不同,spring 是 @Transactional,jta 是 javax.transaction.Transactional,EJB 是 javax.ejb.TransactionAttribute。這個地方應用和 apache dubbo2.7.x 版本解析 dubbo 的 @service 注解是一樣一樣的。關鍵代碼如下,通過 AnnotatedElementUtils 類,這個類在 spring-core 包下,找到注解屬性集 AnnotationAttributes,如果不為空,則包裝成事務屬性集返回
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
AnnotationTransactionAttributeSource
見名知意,這個類是注解事務屬性集的源,怎么理解呢?spring 抽象了獲取事務屬性集的行為,而 AnnotationTransactionAttributeSource 正是 @Transactional 注解方式的事務屬性集收集實現。SpringTransactionAnnotationParser 就是作用於這個里面,用於發現 @Transactiona 注解的方法
TransactionAttributeSourcePointcut
也是見名知意,Pointcut 屬於 aop 的概念范疇,需要了解 spring aop 的知識才能看明白,這個就是 @Transactional 注解的切點,AnnotationTransactionAttributeSource 作用於此,用於尋找 @Transactiona 注解的方法,關鍵代碼如下:
public boolean matches(Method method, Class targetClass) {
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
TransactionInterceptor
事務的攔截器。aop 編程里,有了切入點 Pointcut,就要有通知 advice,我們熟悉的 spring aop 里有前置、后置、環繞、異常等通知類型,TransactionInterceptor 屬於自定義通知模型實現,實現自 Advice 接口,類似於環繞通知,具體見類結構圖,如下:
核心方法如下:
public Object invoke(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);
}
被 @Transactional 注解的方法,如果被 aop 正確的增強了,運行的時候都會進入到這個方法里面,如果你發現事務不生效啊等等問題,可以從這里開始定位真實原因
BeanFactoryTransactionAttributeSourceAdvisor
事務增強器,用於增強添加了 @Transactional 注解的方法,上面提到的這些核心類,最終都作用於這里,用於尋找 @Transactional 注解的方法和織入事務處理邏輯
ProxyTransactionManagementConfiguration
代理事務管理的配置類,上面介紹的這些事務 aop 編程相關的在這個里面組合配置生效的,同時,如果你有特殊的個性化的需求,也可以自定義注冊這個里面的實例。比如我嫌棄 @Transactional 注解太長了,想用 @Tx 注解。沒關系,直接定義個 TransactionAttributeSource 實現,解析 @Tx 的方法,然后注冊到 spring 的上線文中即可。代碼如:
@Configuration(proxyBeanMethods = false)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource,
TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(
TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
事務抽象核心類釋義
PlatformTransactionManager
平台事務管理器,這是 Spring 事務基礎設施中的中心接口。它定義了三個最最基本的事務方法,getTransaction 獲取事務,包含了事務開啟的行為,commit 提交事務,rollback 回滾事務。代碼如下:
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
在 spring-tx 中並沒有提供真正的實現類,只提供了一個抽象派生類 AbstractPlatformTransactionManager,並建議其他實現基於這個派生類,因為它預先實現了定義的傳播行為並處理事務同步處理。子類必須為底層事務的特定狀態實現模板方法,例如: begin、suspend、resume、commit 等。我們平時常見的實現有:JpaTransactionManager、JtaTransactionManager、DataSourceTransactionManager 等。事務管理器和事務 aop 處理的邏輯本身沒有任何耦合,只需將 PlatformTransactionManager 實例注冊到 spring 上下文中即可,事務攔截器會通過獲取到 @Transactional 里的 transactionManager 屬性去上下文中尋找事務管理器,並將其緩存起來,見 TransactionAspectSupport.java 里的 determineTransactionManager 方法
protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
return asPlatformTransactionManager(getTransactionManager());
}
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
PlatformTransactionManager defaultTransactionManager = asPlatformTransactionManager(getTransactionManager());
if (defaultTransactionManager == null) {
defaultTransactionManager = asPlatformTransactionManager(
this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY));
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
TransactionStatus
事務狀態抽象,用這個類的實現來維護當前的事務狀態,spring-tx 里提供了默認的實現 DefaultTransactionStatus。一般情況下這個不需要我們關心,它和 PlatformTransactionManager 是成對存在的,心細的你可能已經發現了,PlatformTransactionManager 里的三個事務行為傳遞的就是 TransactionStatus。我們知道事務 aop 增強了添加 @Transactional 的方法,在執行方法前調用 PlatformTransactionManager.getTransaction 開啟事務, 之后調用 commit 方法提交事務,提交事務的入參 TransactionStatus 就是開啟事務獲得的。參見 TransactionAspectSupport.java 里的 createTransactionIfNecessary 方法
TransactionDefinition
事務定義,對應了 TransactionAttribute,最終通過 aop 得到的 TransactionAttribute 里的屬性會被傳遞到 TransactionDefinition 里,所以 TransactionDefinition 里也包含了所有事務相關的屬性,PlatformTransactionManager.getTransaction 正是通過這個里面的屬性去獲取的事務。AbstractPlatformTransactionManager 派生類里也是通過這個里面的屬性去判斷協調 spring 的事務傳播行為的
結語
當梳理完 spring-tx 模塊的整個結構和工作方式后,仿佛拉開了 spring 事務管理的面紗,很多事務的執行細節一覽無余。很多事務相關的問題也就很容易解釋了。比如常見的類中的方法直接調用方法事務不生效等問題,以及可以非常清晰的理解 spring 的傳播行為的真正含義等。最后預告下,spring 對於多數據源的事務處理解決方案 ChainedTransactionManager