最近遇到了做多数据源多需求,我们多系统是基于多租户多,要求是不同多租户能访问不同多数据源,而达到提高性能和良好的容灾能力。
我们是基于druid+mysql+springboot的:
那我了解到Spring boot提供了AbstractRoutingDataSource的抽象类根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法determineCurrentLookupKey() 决定使用哪个数据源。
AbstractRoutingDataSource
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 源码的介绍:
|
AbstractRoutingDataSource就是DataSource的抽象,基于lookup key的方式在多个数据库中进行切换。重点关注setTargetDataSources,setDefaultTargetDataSource,determineCurrentLookupKey三个方法。那么AbstractRoutingDataSource就是Spring多数据源的关键了。
- setTargetDataSources设置备选的数据源集合
- setDefaultTargetDataSource设置默认数据源
- determineCurrentLookupKey决定当前数据源的对应的key
但是好像3个方法都没有包含切换数据库的逻辑啊!仔细阅读源码发现一个方法,determineTargetDataSource方法,其实它才是获取数据源的实现。源码如下:
|
简单说就是,根据determineCurrentLookupKey获取的key,在resolvedDataSources这个Map中查找对应的datasource!,注意determineTargetDataSource方法竟然不使用的targetDataSources!
那一定存在resolvedDataSources与targetDataSources的对应关系。接着翻阅代码,发现一个afterPropertiesSet方法(Spring源码中InitializingBean接口中的方法),这个方法将targetDataSources的值赋予了resolvedDataSources。源码如下
|
afterPropertiesSet 方法,熟悉Spring的都知道,它在bean实例已经创建好,且属性值和依赖的其他bean实例都已经注入以后执行。
也就是说调用,targetDataSources,defaultTargetDataSource的赋值一定要在afterPropertiesSet前边执行。
AbstractRoutingDataSource简单总结:
AbstractRoutingDataSource,内部有一个Map<Object,DataSource>的域resolvedDataSources
determineTargetDataSource方法通过determineCurrentLookupKey方法获得key,进而从map中取得对应的DataSource。
setTargetDataSources 设置 targetDataSources
setDefaultTargetDataSource 设置 defaultTargetDataSource,
targetDataSources和defaultTargetDataSource 在afterPropertiesSet分别转换为resolvedDataSources和resolvedDefaultDataSource。
targetDataSources,defaultTargetDataSource的赋值一定要在afterPropertiesSet前边执行。
简单写下为代码逻辑:
1、先写一个类继承AbstractRoutingDataSource,实现determineCurrentLookupKey方法,和afterPropertiesSet方法。afterPropertiesSet方法中调用setDefaultTargetDataSource和setTargetDataSources方法之后调用super.afterPropertiesSet。
2、定义一个切面在事务切面之前执行,确定真实数据源对应的key
3、用ThreadLocal传递真实数据源对应的key
4、定义一个druidDataSourceCreator类,每次创建数据源都从这里取
参考文章:1、
2、如果自己不想实现可以使用mybatis-plus的实现https://github.com/baomidou/dynamic-datasource-spring-boot-starter
更建议是自己参考其设计模式进行设计