美文网首页
Spring工程多数据源多个事务管理器导致事务失效

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

作者: 马木木 | 来源:发表于2020-01-06 20:41 被阅读0次

    1.背景

    最新公司在按产品线拆分数据库做Mysql多活,导致一个工程中有多个数据源,我们产品线是最先拆出来的所以这些数据源,事务管理器的配置都是默认的,正常使用,但是在其他产品线加入新的数据源及事务管理器后发现事务失效了,怀疑和配置方式有关。

    2.原因分析

    image
    话不多说先上代码为敬,至于为啥打码,你懂的,com后边一般来说是公司名称了呀。

    上图的1是后来的,后来者居上嘛,pxhTransactionManager是没有问题的,因为好名字transactionManager已经被我们占了嘛(首发除了探坑还是有好处的),问题应该就出在了又定义了一次<tx:annotation-driven>标签。

    这里边挺奇怪的,一般大家在工程中就直接是<tx:annotation-driven/>声明下这个注解就行了,为啥还要写transaction-manager属性呢,这里我们推测(其实就是这样)这是指定事务的默认的管理器,既然是默认那就有个默认的名字,没错如大家所想默认的名字就是transactionManager,其实IDEA已经给我们提示了,如下图

    image

    无用的声明默认属性,而且鼠标放上去Alt+Enter第一个提示是可以删除的

    定义多次tx标签就是很奇怪的事情,这可以在单例天下的Spring中,而且我们根据结果和代码可以猜测,<tx:annotation-driven>标签采取的是先入为主的策略,导致我们默认调用的事务管理器变成了pxhTransactionManager,然而这并不是我们代码需要调用的数据源,所以回滚无效

    3.代码分析

    猜测必须要以代码为依据,既然标签是定义在XML文件中的,那么我们就从Spring解析我们的配置文件开始看起,我们从SpringWeb.xml文件中定义的启动窗口开始,就是大家都知道的ContextLoaderListener

    image

    Listener 均是ServletContextListener的实现类在容器启动的时候自行执行contextInitialized方法,进入contextLoader.initWebApplicationContext方法

    image

    这个方法主要是初始化容器上下文,具体的bean加载等方法在configureAndRefreshWebApplicationContext()方法中

    image

    进入wac.refresh()方法,这里的wac是类是XmlWebApplicationContext,上边的就不说了,太烦了没看懂……

    image

    XmlWebApplicationContext 这个类的继承很复杂,这里的refresh()
    实际的代码在AbstractApplicationContext类中

    image

    直奔主题进入obtainFreshBeanFactory()方法

    image

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

    image

    进入XmlWebApplicationContext.loadBeanDefinitions()方法

    image

    进入AbstractBeanDefinitionReader.loadBeanDefinitions()的一系列重载方法,然后进入AbstractBeanDefinitionReader类的loadBeanDefinitions(String location, Set<Resource> actualResources)方法

    image

    进入XmlBeanDefinitionReader类的loadBeanDefinitions(EncodedResource encodedResource)方法

    image

    经过XmlBeanDefinitionReader类的doLoadBeanDefinitions()方法进入registerBeanDefinitions()方法,

    image

    进入DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法

    image

    然后进入parseBeanDefinitions()方法

    image

    像<tx:>这样的标签属于自定义标签走BeanDefinitionParserDelegate.parseCustomElement()方法

    image

    重点在于通过命名空间找到对应的处理类,进入DefaultNamespaceHandlerResolver类的resolve()方法

    image

    下边是handlerMappings命名空间和处理类的对应关系

    image

    进入TxNamespaceHandler类,可以看出来初始化了几个关键的类

    image

    回到刚才的入口,进入NamespaceHandlerSupport.parse()方法

    image image

    可以看出来需要执行AnnotationDrivenBeanDefinitionParser.parse()方法

    image

    可以发现我们熟悉的包都有身影出现,这里我们猜也是工厂模式的一种了,这里其实就是各种需要引入命名空间的标签的处理类了。我们需要进入的是
    spring-tx包,在AnnotationDrivenBeanDefinitionParser的内部静态类AopAutoProxyConfigurer的静态方法configureAutoProxyCreator是关键,这边也解释了为什么先入为主,

    image

    只有当不存在这个类型的bean的时候,才会对bean初始化并且注册到容器中,而xml解析是从上到下的,所以我们被放在下边的事务管理器并不会起到应有的作用,而是被无情的抛弃。

    同样在这部分的代码解释了transaction-manager标签属性的解析过程,默认值也是在此设置的

    image image image

    TransactionInterceptor类的invoke()方法是事务代理对象的具体执行者,具体的代码在TransactionAspectSupport.invokeWithinTransaction()类,

    image

    上图的completeTransactionAfterThrowing方法,决定了在什么异常发生时回滚,如果@Transactional注解没有指定rollbackFor属性的话,就进入DefaultTransactionAttribute,s所以只会在RuntimeException类型异常和Error时回滚

    image image

    4.解决方案

    其实解决方案网上都有,就是在@Transactional注解里边指定对应的事务管理器,例如
    @Transactional("pxhTransactionManager")
    @Transactional("transactionManager")

    具体原因看下边的代码就知道了在TransactionAspectSupport类中

    image image

    相关文章

      网友评论

          本文标题:Spring工程多数据源多个事务管理器导致事务失效

          本文链接:https://www.haomeiwen.com/subject/vpqlactx.html