数据源切换方法
Springboot提供了AbstractRoutingDataSource抽象类,类名意思是数据源路由,让用户可以选择根据需要切换当前数据源
该类提供了一个抽象方法determineCurrentLookupKey(), 切换数据源时springboot会调用这个方法,所以只需要实现该方法,在该方法中返回需要切换的数据源名称即可
源码解读
1.从类关系图中可以看出AbstractRoutingDataSource类实现的是DataSource接口(非最底层),
其要求实现一个方法getConnection(),即获取DB连接
2.AbstractRoutingDataSource实现了这两个方法
其中determineTargetDataSource()调用determineCurrentLookupKey()方法,取到当前设定的查找键,通过查找键在上下文this.resolvedDataSources属性中尝试获取DataSource对象,这个对象即当前连接的数据源
3.那么this.resolvedDataSources在哪里维护?
AbstractRoutingDataSource类实现了InitializingBean类的afterPropertiesSet()方法,
在bean的所有属性设置完成后变会调用此方法,可以看到this.resolvedDataSources从this.targetDataSources取的信息;
所以只需要改变this.targetDataSources,并且触发afterPropertiesSet(),即可改变this.resolvedDataSources;后续改变determineCurrentLookupKey()的返回值(key),在调用getConnection()时即可获取到指定的数据源
多租户业务背景
多租户业务场景下,往往每个租户都独立一个数据库(是否独立数据源实例根据实际需要处理),每个租户的数据在数据库层面先做了隔离,在开展详细业务编写时就可以不用考虑不同租户的数据会混淆。但是随之而来的就是数据源灵活切换的需求,需要封装一套方法,在业务编写时可以根据提供的租户代码便捷的切换到对应的数据源
提供的切换方式
1.注解方式切换
提供一个注解,可以根据租户代码切换,也可以根据配置文件中写定的数据源名称切换
2.直接调用方法方式切换
提供一个租户rds切换类,在编写业务代码时调用方法切换,该方式可以让租户代码以变量形式传递,无需提前知道
实现步骤概要
1.添加pom依赖、配置数据源信息
2.编写数据源配置类,将数据源配置信息注入到容器
3.编写DynamicDataSource类继承AbstractRoutingDataSource抽象类,维护当前数据源信息,提供切换方法
4.编写租户rds切换类,业务切换数据源时统一调用此类
5.编写自定义注解
6.编写切面类,将连接点直接设定在编写的自定义注解上,根据参数等调用rds切换类切换数据源
7.异常类、异常枚举类,规范异常抛出
详细步骤
1.pom依赖添加、配置数据源信息
pom.xml
1 | xml复制代码<dependencies> |
application.yml
1 | yaml复制代码# 主配置 |
1.配置里面包含了一些druid的配置,可以根据业务需要自行配置
2.其中,spring.datasource.druid.master为主数据源,也是配置库数据源,租户库数据源连接信息会在配置库中获取,spring.datasource.druid.db1为递增数据源,db1可以命名为具体的业务库名称,这里仅仅方便理解取名为db1
2.编写数据源配置类,将数据源配置信息注入到容器
数据源配置类DataSourceConfig
1 | java复制代码@Configuration |
数据源常量类
1 | java复制代码public class DataSourceConstant { |
此处先往DynamicDataSource.dataSourceMap将两个配置好的数据源连接信息写入,并设置到动态数据源对象中,这个值最终会在afterPropertiesSet中被设置到resolvedDataSources上
3.编写DynamicDataSource类继承AbstractRoutingDataSource抽象类,维护当前数据源信息,提供切换方法
动态数据源类DynamicDataSource
1 | java复制代码public class DynamicDataSource extends AbstractRoutingDataSource { |
- 维护了一个key,用于表示当前用的是哪个数据源
- 维护了一个map,用于springboot获取数据源信息
- 重写determineCurrentLookupKey方法,可参照上面源码解读理解
4.编写租户rds切换类,业务切换数据源时统一调用此类
RdsConfig类,该javabean描述rds的连接信息
1 | java复制代码@Data |
具体rds切换服务类:TenantRdsServiceImpl类,实现TenantRdsService接口,这里不贴出来了
1 | java复制代码@Service |
1.这里用到了两张表,一张是租户表(tenant)用于存储租户代码与rds的对应关系,另一张是DB连接信息(rds)表,用于存储数据源连接信息,具体的mapper和javabean的代码这里就不贴出来了,根据需求建表具体实现即可
2.提供了三个方法分别是根据租户代码获取rds连接信息,根据租户代码切换rds,根据数据源名称切换rds,切换方法中对当前连接信息做了判断,不会重复切换,也不会重复查配置库获取rds信息
5.编写自定义注解
自定义注解 @SwitchMasterRds
1 | java复制代码/** |
自定义注解 @SwitchRds
1 | java复制代码/** |
1.SwitchRds注解既可以用租户代码切换rds,又可以使用数据源名称切换
2.SwitchMasterRds注解是为了方便切换成主数据源而添加的
6.编写切面类
SwitchMasterRds注解的切面类SwitchMasterRdsAspect
1 | java复制代码@Aspect |
SwitchRds注解的切面类SwitchRdsAspect
1 | java复制代码@Aspect |
这里将连接点直接设定在编写的自定义注解上,根据参数等调用rds切换类切换数据源
7.异常类、异常枚举类
ErrorInfo接口,规范异常枚举类
1 | java复制代码public interface ErrorInfo { |
处理异常枚举类,将所有错误类型以及错误代码枚举出来
1 | java复制代码/** |
处理异常基类,所有的处理异常全部继承该类,其保存ErrorInfo信息,可以方便获取错误代码等
1 | java复制代码/** |
具体的异常类,直接继承处理异常基类,文中主动抛出的异常全是这样方式编写,这里就不一一列举了
1 | java复制代码/** |
使用
1.注解方式
1 | java复制代码@RestController |
2.业务代码使用方式
1 | java复制代码try { |
本文转载自: 掘金