转载

Spring工程多数据源多个事务管理器导致事务失效

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工程多数据源多个事务管理器导致事务失效
原文  https://juejin.im/post/5e12a1a25188253a782d8518
正文到此结束
Loading...