一、 略过不讲的部分

1、 动态数据源AbstractRoutingDataSource的使用;

2、 非事务的情况下动态数据源基于AOP的切换

3、 注解@Order、@Transational的作用

4、 Bean的生命周期

以上问题需要掌握,可自行百度

二、 动态数据库源与主从切换中碰到的问题与解决

2.1 动态数据源碰到的问题

笔者是这样完成动态数据源和事务切换的,如图一:

spring 动态数据源表 spring动态数据源切换 高并发_spring 动态数据源表

1、在非事务的的调用过程中,调用链是这样的:
(Before)AOP->DefaultSqlSession–>SimpleExecutor–>BaseExecutor.getConnection()–>SpringManagedTransaction.getConnection()—>连接为空–>AbstractRoutingDataSource.getConnection()–>拿到beforeAOP中注入的datasource的key。

因此在非事务的情况下,都能动态切换数据源。
2、而在事务中,调用过程发生了变化,期初调用链是这样的:
@transactional–>TransactionInterceptor.interpter()–>TransactionAspectSupport.createTransactionIfNecessary()–>AbstractPlatformTransactionManager.getTransaction()–>DataSourceTransactionManager.doBegin()–>AbstractRoutingDataSource.determineTargetDataSource()[lookupKey==null去拿默认的Datasource, 不为空则使用获取到的连接]
–>DataSourceTransactionManager.setTransactional()—>(Before) @AOP

从上面的调用链来看,AbstractPlatformTransactionManager.getTransaction()获得的连接为默认的连接,而非切换后的连接。这样直接导致table not exists。

2.2 解决方案与原理

从上面问题来看,解决必须要先进行动态数据源的切换,再进行TransactionInterceptor的拦截,也就是自定义的DataSourceChange的拦截早于TransactionInterceptor的处理。

在Bean的创建函数中,函数是这样的,如图二所示

spring 动态数据源表 spring动态数据源切换 高并发_spring_02

图二、Bean的创建

我们进入doCreateBean,在doCreateBean中,我们看这段代码,如图三所示:

spring 动态数据源表 spring动态数据源切换 高并发_java_03

图三、Bean的创建过程

我们再进入Bean的初始化工作函数,其中最后一步就是主要加载Interceptors,如图四所示。

spring 动态数据源表 spring动态数据源切换 高并发_java_04

图四,加载拦截器Beans

我们进入这个方法,如图四所示

spring 动态数据源表 spring动态数据源切换 高并发_spring 动态数据源表_05

图五 加载拦截器

我们从断点处往下看第四行,对拦截器进行了排序,进入排序的函数,如图六所示

spring 动态数据源表 spring动态数据源切换 高并发_spring_06

图六 排序函数

我们再进入排序的比较器PartiallyComparableAdvisorHolder,如图七所示

spring 动态数据源表 spring动态数据源切换 高并发_加载_07

图七 比较器的排序规则
最后进入AspectJPrecedenceComparator,真相大白,因此我们也找到了Order的代码,从代码来看,如果没有指定Order则返回2147483647

spring 动态数据源表 spring动态数据源切换 高并发_spring 动态数据源表_08

图八 AspectJPrecedenceComparator中的Order排序规则

而TransactionInterceptor并未指定Order,因此顺序为2147483647,因此只要指定@DataSource的@Order小于2147483647即可。

三、 动态数据源ReadOnly事务的主从

那么这里还有一个问题,切换数据源只处理业务数据库的切换,在切片中并未处理ReadOnly主从,如果到Repository或Mybatis插件中再处理主从,而DataSourceTransactionManager显然连接还是错误的。因此必须在ReadOnly事务中切换到从库,笔者采用重载doBegin和doCleanupAfterCompletion的方式,如图九所示。

spring 动态数据源表 spring动态数据源切换 高并发_spring_09

图九 ReadOnly的事务切换主从库
最后定义DataSourceTransactionManager @Bean为我们重载后的新类即可。