Spring工程多數據源多個事務管理器導致事務失效

NO IMAGE

1.背景

最新公司在按產品線拆分數據庫做Mysql多活,導致一個工程中有多個數據源,我們產品線是最先拆出來的所以這些數據源,事務管理器的配置都是默認的,正常使用,但是在其他產品線加入新的數據源及事務管理器後發現事務失效了,懷疑和配置方式有關。

2.原因分析

Spring工程多數據源多個事務管理器導致事務失效

話不多說先上代碼為敬,至於為啥打碼,你懂的,com後邊一般來說是公司名稱了呀。

上圖的1是後來的,後來者居上嘛,pxhTransactionManager是沒有問題的,因為好名字transactionManager已經被我們佔了嘛(首發除了探坑還是有好處的),問題應該就出在了又定義了一次<tx:annotation-driven>標籤。

這裡邊挺奇怪的,一般大家在工程中就直接是<tx:annotation-driven/>聲明下這個註解就行了,為啥還要寫transaction-manager屬性呢,這裡我們推測(其實就是這樣)這是指定事務的默認的管理器,既然是默認那就有個默認的名字,沒錯如大家所想默認的名字就是transactionManager,其實IDEA已經給我們提示了,如下圖

Spring工程多數據源多個事務管理器導致事務失效

無用的聲明默認屬性,而且鼠標放上去Alt+Enter第一個提示是可以刪除的

定義多次tx標籤就是很奇怪的事情,這可以在單例天下的Spring中,而且我們根據結果和代碼可以猜測,<tx:annotation-driven>標籤採取的是先入為主的策略,導致我們默認調用的事務管理器變成了pxhTransactionManager,然而這並不是我們代碼需要調用的數據源,所以回滾無效

3.代碼分析

猜測必須要以代碼為依據,既然標籤是定義在XML文件中的,那麼我們就從Spring解析我們的配置文件開始看起,我們從SpringWeb.xml文件中定義的啟動窗口開始,就是大家都知道的ContextLoaderListener

Spring工程多數據源多個事務管理器導致事務失效

Listener 均是ServletContextListener的實現類在容器啟動的時候自行執行contextInitialized方法,進入contextLoader.initWebApplicationContext方法

Spring工程多數據源多個事務管理器導致事務失效

這個方法主要是初始化容器上下文,具體的bean加載等方法在configureAndRefreshWebApplicationContext()方法中

Spring工程多數據源多個事務管理器導致事務失效

進入wac.refresh()方法,這裡的wac是類是XmlWebApplicationContext,上邊的就不說了,太煩了沒看懂……

Spring工程多數據源多個事務管理器導致事務失效

XmlWebApplicationContext 這個類的繼承很複雜,這裡的refresh()
實際的代碼在AbstractApplicationContext類中

Spring工程多數據源多個事務管理器導致事務失效

直奔主題進入obtainFreshBeanFactory()方法

Spring工程多數據源多個事務管理器導致事務失效

loadBeanDefinitions(beanFactory)方法解析XML文件,進入AbstractXmlApplicationContext類中的loadBeanDefinitions()方法中

Spring工程多數據源多個事務管理器導致事務失效

進入XmlWebApplicationContext.loadBeanDefinitions()方法

Spring工程多數據源多個事務管理器導致事務失效

進入AbstractBeanDefinitionReader.loadBeanDefinitions()的一系列重載方法,然後進入AbstractBeanDefinitionReader類的loadBeanDefinitions(String location, Set<Resource> actualResources)方法

Spring工程多數據源多個事務管理器導致事務失效

進入XmlBeanDefinitionReader類的loadBeanDefinitions(EncodedResource encodedResource)方法

Spring工程多數據源多個事務管理器導致事務失效

經過XmlBeanDefinitionReader類的doLoadBeanDefinitions()方法進入registerBeanDefinitions()方法,

Spring工程多數據源多個事務管理器導致事務失效

進入DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法

Spring工程多數據源多個事務管理器導致事務失效

然後進入parseBeanDefinitions()方法

Spring工程多數據源多個事務管理器導致事務失效

tx:這樣的標籤屬於自定義標籤走BeanDefinitionParserDelegate.parseCustomElement()方法

Spring工程多數據源多個事務管理器導致事務失效

重點在於通過命名空間找到對應的處理類,進入DefaultNamespaceHandlerResolver類的resolve()方法

Spring工程多數據源多個事務管理器導致事務失效

下邊是handlerMappings命名空間和處理類的對應關係

Spring工程多數據源多個事務管理器導致事務失效

進入TxNamespaceHandler類,可以看出來初始化了幾個關鍵的類

Spring工程多數據源多個事務管理器導致事務失效

回到剛才的入口,進入NamespaceHandlerSupport.parse()方法

Spring工程多數據源多個事務管理器導致事務失效

Spring工程多數據源多個事務管理器導致事務失效

可以看出來需要執行AnnotationDrivenBeanDefinitionParser.parse()方法

Spring工程多數據源多個事務管理器導致事務失效

可以發現我們熟悉的包都有身影出現,這裡我們猜也是工廠模式的一種了,這裡其實就是各種需要引入命名空間的標籤的處理類了。我們需要進入的是
spring-tx包,在AnnotationDrivenBeanDefinitionParser的內部靜態類AopAutoProxyConfigurer的靜態方法configureAutoProxyCreator是關鍵,這邊也解釋了為什麼先入為主,

Spring工程多數據源多個事務管理器導致事務失效

只有當不存在這個類型的bean的時候,才會對bean初始化並且註冊到容器中,而xml解析是從上到下的,所以我們被放在下邊的事務管理器並不會起到應有的作用,而是被無情的拋棄。

同樣在這部分的代碼解釋了transaction-manager標籤屬性的解析過程,默認值也是在此設置的

Spring工程多數據源多個事務管理器導致事務失效

Spring工程多數據源多個事務管理器導致事務失效

Spring工程多數據源多個事務管理器導致事務失效

TransactionInterceptor類的invoke()方法是事務代理對象的具體執行者,具體的代碼在TransactionAspectSupport.invokeWithinTransaction()類,

Spring工程多數據源多個事務管理器導致事務失效

上圖的completeTransactionAfterThrowing方法,決定了在什麼異常發生時回滾,如果@Transactional註解沒有指定rollbackFor屬性的話,就進入DefaultTransactionAttribute,s所以只會在RuntimeException類型異常和Error時回滾

Spring工程多數據源多個事務管理器導致事務失效

Spring工程多數據源多個事務管理器導致事務失效

4.解決方案

其實解決方案網上都有,就是在@Transactional註解裡邊指定對應的事務管理器,例如
@Transactional("pxhTransactionManager")
@Transactional("transactionManager")

具體原因看下邊的代碼就知道了在TransactionAspectSupport類中

Spring工程多數據源多個事務管理器導致事務失效

Spring工程多數據源多個事務管理器導致事務失效

相關文章

SpringBoot系列教程Mybatis+註解整合篇

六年碼農生涯的2019總結:君子坐而論道,少年起而行之

一次kafka消息堆積問題排查

第三屆SEEConf2020演講資料(PPT)