作者: 卖釹孩的小火柴

出处: https://www.cnblogs.com/eaglecai/p/13168255.html/>

关于作者:瓜皮码农,请多多关照

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接

SpringAOP事务传播行为以及动态数据源切换的衔接

我们都知道spring事务管理是基于AOP实现的,那么具体它是怎样的一个调用链逻辑呢?当然关于service代理类生成时机,以及拦截器注册实际是在IOC容器初始化的时候完成的,这里不再细说了。需要了解的可以去源码中打debug走一下IOC容器初始化的流程

  • 1.首先,在执行service方法前,肯定是CGLIB代理类去执行,需要去执行AOP的拦截器(执行增强逻辑,这里指事务管理拦截器)

 

 

  • 2. org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 拦截器拦截方法,会先去获取事务,然后对service方法进行环绕执行(实现一个横切逻辑),成功则提交事务,失败则回滚,也可能是不携带事务执行

 

 

  • 3.org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction ,这里是针对上面createTransactionIfNecessary方法中真正获取事务动作

 

 

 

  • 4.org.springframework.transaction.support.AbstractPlatformTransactionManager#doGetTransaction 获取当前事务对象DataSourceTransactionObject,根据dataSource作为key去从容器中获取ConnectionHolder(这里面封装了真正的mysql的Connection,第一次取到是空的)

 

 

 

  • 5.org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin 在前面取不到当前存在的事务,这时候就需要去创建事务,new一个DataSourceTransactionObject以便后面使用
  • 6.获取到新创建的事务后,最终执行sql,mybatis执行prepareStatement才会去真正进行数据库连接openConnection,并将connection缓存至事务对象中。
  • 7.至此,第一个service1方法A执行完毕,我叫它阶段一完成,提交事务

下面开始阶段二,动态数据源切换问题,准备执行另一个service的方法B。下面首先看一下service2方法B的配置,这里我配置了litemall.service包下的方法都先挂了一个动态数据源切换的拦截器,属性就是要切换的slave数据源,拦截器拦截到方法后,要将DynamicDataSourceKeyHolder的key值设置成slave(即数据源配置切为slave

 

 

 

 

  • 8.这里说一下这个DynamicDataSource,配置动态数据源切换,还需要自定义数据源配置类,继承AbstractRoutingDataSource,重写钩子方法determineCurrentLookupKey每次获取数据库连接时(是在mybatis

    执行prepareStatement真正后去数据库连接时,会去从SpringManagedTransaction中获取connection,取不到则重新读配置获取连接openConnection),会执行determineCurrentLookupKey()方法决定当前使用的数据源配置

 

开始方法B执行逻辑

  • 9.org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 再次执行该方法

  • 10.org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 获取事务 

  • 11.org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction 这个时候取当前事务对象,返回的是前一次方法执行缓存下来的事务

  • 通过isExistingTransaction(transaction)判断是一个已存在的事务,下面进一步根据事务传播行为进行处理
  • 12.org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction  这里很啰嗦,如果是PROPAGATION_REQUIRED()默认也是这个,就直接返回了事务对象了;但是如果是PROPAGATION_REQUIRES_NEW就会重新获取事务(根据当前配置项获取新的connection)

  • 13.org.springframework.transaction.support.AbstractPlatformTransactionManager#doBegin 这里去重新创建了事务对象,新的事务对象中Connection为空了,所以后面mybatis在执行sql前需要读DataSourceHolder新建一个Connection,至此数据源切换完成。

    • mybatis执行prepareStatement真正后去数据库连接时,会去从SpringManagedTransaction中获取connection,取不到则重新读配置获取连接openConnection,而openConnection时又会去读一次determineCurrentLookupKey(),加载当前数据源配置(数据源配置已经在前面拦截器执行时切换成了slave)获得一个新的数据库连接Connection

 

PS:这里说一个问题,如果你配置了切换动态源的point-cut方法,但是同时事务传播行为配置的PROPAGATION_REQUIRED,那么这样是无法完成数据源切换的,因为它每次是获取了已存在事务并加入,而原事务中携带的Connection是之前的数据库连接

 

 

下面是我的spring.xml配置信息

 

 

 

posted @ 2020-06-20 12:35  卖釹孩的小火柴  阅读(231)  评论(0)    收藏  举报