前言
很多人認爲事務很簡單,但是往往在工作中遇到一些事務的坑(尤其是事務方法中嵌套其它事務方法一起使用時)之後,我們卻不知道問題產生的原因和如何有效的解決。
這就需要去分析 Spring的核心源碼 ,最終踏實地找到問題的原因和解決思路。
註解事務運行流程
先來看Spring事務的底層運行流程
核心對象關係
一、事務配置相關
TransactionManagementConfigurationSelector :配置啓動事務啓動(EnableTransactionManagement)時,導入註冊的配置bean。
它包括AutoProxyRegistrar和ProxyTransactionManagementConfiguration兩大配置塊。
AutoProxyRegistrar :負責依賴注入事務的相關屬性配置和注入事務入口類(InfrastructureAdvisorAutoProxyCreator類);
ProxyTransactionManagementConfiguration :負責注入事務相關的Bean, 包括:
- 事務切面Bean(BeanFactoryTransactionAttributeSourceAdvisor)
- TransactionAttributeSource(事務配置屬性bean)
- TransactionInterceptor(事務攔截器bean);
二、事務運行攔截相關
- AopProxy:Spring AOP 動態代理的基接口,負責定義Proxy的基礎行爲;
- MethodInterceptor:Spring AOP調用鏈中攔截器的內部核心接口,所有類型的切面最終都會最終包裝成此接口觸發統一攔截;
- TransactionInterceptor:Spring事務攔截器的核心業務實現,AOP調用鏈也最終觸發它的invoke方法;
- TransactionManager:Spring管理的基接口,作爲子接口上層接口區分,並沒有定義實際的事務行爲能力;
- PlatformTransactionManager:繼承TransactionManager,定義事務和基礎行爲;
- AbstractPlatformTransactionManager:負責實現整個事務管理和運行過程中的公共行爲和通用實現邏輯,它繼承至PlatformTransactionManager接口;
源碼實現
接下來我們來分塊分析一下,Spring事務的源碼,其中的一些坑和重要結論會在這個過程分享。
一、事務配置
TransactionManagementConfigurationSelector.selectImports()負責定義外部加入spring容器的配置類
此方法最終在ConfigurationClassParser中被解析並最終實例化爲bean
AutoProxyRegistrar.registerBeanDefinitions()把InfrastructureAdvisorAutoProxyCreator註冊beandefinition
ProxyTransactionManagementConfiguration.transactionAdvisor()注入事務切面
二、事務創建
實際運行入口之一(還有cglib):JdkDynamicAopProxy..invoke()
ReflectiveMethodInvocation.proceed()切面調用鏈處理核心方法
TransactionInterceptor.invoke()從這裏觸發事務攔截
TransactionAspectSupport.invokeWithinTransaction()實現Spring事務的核心業務
TransactionAspectSupport.determineTransactionManager()定義指定的事務管理器
對於事務管理器,默認使用DataSourceTransactionManager(可配置數據源的事務管理器),也可自定義事務管理器,然後配置數據源即可。
createTransactionIfNecessary()創建事務信息TransactionInfo:其中包括數據源、事務連接(ConnectionHolder)、事務狀態、連接緩存等;
DataSourceTransactionManager.doGetTransaction()獲取事務對象:裏面包含連接包裝和緩存
TransactionSynchronizationManager.getResource()連接線程級緩存:確保當前線程拿到唯一的數據連接
AbstractPlatformTransactionManager.startTransaction()開啓一個新事務
只有newTransaction爲true時,spring纔會做實際的提交或回滾,這裏是一個很重要的點。很多嵌套事務的坑,都是這裏沒有理解清楚。
DataSourceTransactionManager.doBegin()開啓事務核心源碼
TransactionSynchronizationManager.bindResource()設置當前連接和數據源的線程級綁定
三、事務回滾
對於非編程式事務而言,Spring事務的核心實現其實就是用try / catch 包裹事務提交和回滾的範式而已,但提交和回滾裏面的包裝大有講究。
TransactionAspectSupport.completeTransactionAfterThrowing()事務回滾實現
以上截圖中doSetRollbackOnly(),不會在連接上實際設置回滾點,只打個標記(當前事務需要回滾, commit時會使用該標記)!
四、事務提交
AbstractPlatformTransactionManager.commit()事務實際提交源碼這裏
cleanupAfterCompletion()回收連接或恢復事務,這個點耐人尋味:當事務傳播屬性爲Require_New時,會暫時掛起之前的連接,然後創建新連接;當新連接提交或回滾後,通過這個方法恢復之前連接和狀態!!!!
OK事務到這裏,我根據源碼分享一些關於嵌套子事務的甜點:
- 嵌套事務過程,中間有任何異常,只要沒被屏蔽,則都會上拋,不會再執行後續代碼;
- 只要設置Require_New事務傳播屬性,則每個Transactional註解方法內部都會單獨提交或回滾;
- 嵌套事務會在除首個Transactional註解外的子事務中創建savepoint回滾點;
- 設置savepoint回滾點後,回滾時會回滾包括當前方法之前的所有事務;若只需回滾當前方法,則在最外層事務方法加try{}catch(){//不拋出異常};
- 每個事務的對象都是不一樣的,事務狀態可能不一樣,但連接可能是同一個;
總結
今天的Spring事務源碼就暫時分享到這裏,事務這塊只有一層的事務很簡單,但當嵌套了多個(層)子事務(而且每個子事務的事務傳播屬性可能不一樣的情況下)時就當另當別論了。
需要我們根據源碼的規律去分析每層子事務該如何流轉!!所以需要把這塊的源碼搞熟,遇到複雜的嵌套事務分析原因,那問題就不大了。