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

1.背景

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

2.緣由分析

話很少說先上代碼爲敬,至於爲啥打碼,你懂的, com後邊通常來講是公司名稱了呀。

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

這裏邊挺奇怪的,通常你們在工程中就直接是<tx:annotation-driven/>聲明下這個註解就好了,爲啥還要寫transaction-manager屬性呢,這裏咱們推測(其實就是這樣)這是指定事務的默認的管理器,既然是默認那就有個默認的名字,沒錯如你們所想默認的名字就是transactionManager,其實IDEA已經給咱們提示了,以下圖數據庫

無用的聲明默認屬性,並且鼠標放上去Alt+Enter第一個提示是能夠刪除的app

定義屢次tx標籤就是很奇怪的事情,這能夠在單例天下的Spring中,並且咱們根據結果和代碼能夠猜想,<tx:annotation-driven>標籤採起的是先入爲主的策略,致使咱們默認調用的事務管理器變成了pxhTransactionManager,然而這並非咱們代碼須要調用的數據源,因此回滾無效spa

3.代碼分析

猜想必需要以代碼爲依據,既然標籤是定義在XML文件中的,那麼咱們就從Spring解析咱們的配置文件開始看起,咱們從SpringWeb.xml文件中定義的啓動窗口開始,就是你們都知道的ContextLoaderListener3d

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

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

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

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

直奔主題進入obtainFreshBeanFactory()方法

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

進入XmlWebApplicationContext.loadBeanDefinitions()方法

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

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

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

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

而後進入parseBeanDefinitions()方法

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

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

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

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

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

能夠看出來須要執行AnnotationDrivenBeanDefinitionParser.parse()方法

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

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

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

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

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

4.解決方案

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

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

相關文章
相關標籤/搜索