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配置信息


浙公网安备 33010602011771号