AbstractRoutingDataSource 实现动态数据源切换原理简单分析
AbstractRoutingDataSource 实现动态数据源切换原理简单分析
写在前面,项目中用到了动态数据源切换,记录一下其运行机制。
代码展示
下面列出一些关键代码,后续分析会用到
- 数据配置
@Configuration
@PropertySource({ "classpath:jdbc.yml" })
@EnableTransactionManagement(proxyTargetClass = true)
public class DataConfig {
@Autowired
private Environment env ;
/**
* 将jdbc相关的异常转换为spring的异常类型
*/
@Bean
public BeanPostProcessor persistenceTransLation(){
return new PersistenceExceptionTranslationPostProcessor() ;
}
/**
* 多数据源
* @return
*/
@Bean
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object,Object> sourceMap = new HashMap<>();
//取得所有的datasource,DataSourceEnum里存放数据源的唯一标识
EnumSet<DataSourceEnum> enums = EnumSet.allOf(DataSourceEnum.class);
for(DataSourceEnum dataSource:enums){
// map存放数据源的key和数据源
sourceMap.put(dataSource.getKey(),generateDataSource(dataSource.getKey()));
}
// 重点
dynamicDataSource.setTargetDataSources(sourceMap);
dynamicDataSource.setDefaultTargetDataSource(sourceMap.get(DataSourceEnum.TEST.getKey()));
return dynamicDataSource;
}
// 读取配置文件,创建数据源对象
private EncryptDataSource generateDataSource(String key){
EncryptDataSource dataSource
= new EncryptDataSource();
key = key.toLowerCase() ;
String url = "jdbc.url."+key;
String username = "jdbc.username."+key;
String password = "jdbc.password."+key;
dataSource.setDriverClassName("com.sybase.jdbc4.jdbc.SybDataSource");//SybDriver
dataSource.setUrl(env.getProperty(url));
dataSource.setUsername(env.getProperty(username));
dataSource.setPassword(env.getProperty(password));
//配置连接池
dataSource.setInitialSize(Integer.parseInt(env.getProperty("jdbc.initialSize")));
dataSource.setMaxIdle(Integer.parseInt(env.getProperty("jdbc.maxIdle")));
dataSource.setMinIdle(Integer.parseInt(env.getProperty("jdbc.minIdle")));
return dataSource;
}
}
- 自定义数据源类
public class DynamicDataSource extends AbstractRoutingDataSource {
// 存放数据源的id(唯一标识)
private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>() ;
// 重点
@Override
protected Object determineCurrentLookupKey() {
return dataSourceHolder.get();
}
// 切换数据源
public static void router(String sourceKey){
if(StrUtil.isEmpty(sourceKey)){
return;
}
if(DataSourceEnum.getSourceByKey(sourceKey)!=null){
//根据法院代码切换
dataSourceHolder.set(DataSourceEnum.getSourceByKey(sourceKey));
}
}
……
}
- 数据源配置(jdbc.yml)
#测试库
jdbc.url.test: jdbc:sybase:Tds:xxx.xxx.xxx.xxx:xx/JUDGE?charset=cp936
jdbc.username.test: fymis
jdbc.password.test: xx
原理分析
第一部分已将关键代码列出,该部分通过修改后即可实现数据源的切换功能。下面来分析一下流程。
AbstractRoutingDataSource 类解析
只列出了部分方法,需要详细代码请自行移步源码
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Nullable
private Map<Object, Object> targetDataSources;// 目标数据源map
@Nullable
private Object defaultTargetDataSource;// 默认数据源
private boolean lenientFallback = true;
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
@Nullable
private Map<Object, DataSource> resolvedDataSources;
@Nullable
private DataSource resolvedDefaultDataSource;
public AbstractRoutingDataSource() {
}
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
this.defaultTargetDataSource = defaultTargetDataSource;
}
// 初始化 Bean 时执行
public void afterPropertiesSet() {
if (this.targetDataSources == null) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
} else {
// 将targetDataSources属性的值赋值给resolvedDataSources,后续需要用到resolvedDataSources
this.resolvedDataSources = new HashMap(this.targetDataSources.size());
this.targetDataSources.forEach((key, value) -> {
Object lookupKey = this.resolveSpecifiedLookupKey(key);
DataSource dataSource = this.resolveSpecifiedDataSource(value);
this.resolvedDataSources.put(lookupKey, dataSource);
});
if (this.defaultTargetDataSource != null) {
this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}
}
// 重写了 getConnection 方法,ORM 框架执行语句前会调用该处
@Override
public Connection getConnection() throws SQLException {
return this.determineTargetDataSource().getConnection();
}
// 同上
@Override
public Connection getConnection(String username, String password) throws SQLException {
return this.determineTargetDataSource().getConnection(username, password);
}
// 重点
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
// 调用我们重写的determineCurrentLookupKey方法,返回的是数据源的唯一标识
Object lookupKey = this.determineCurrentLookupKey();
// 从map中查询改标识对应的数据源,然后返回该数据源,打开对应连接
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}
// 钩子方法,供我们重写
@Nullable
protected abstract Object determineCurrentLookupKey();
}
总结与闲谈
综上,可以列出以下几点描述整个流程:
- 自定义类继承
AbstractRoutingDataSource(后文称 ARDS),重写determineCurrentLookupKey(),返回数据源的唯一标识; - 将数据源名称和数据源封装为 map,调用 ARDS 类的
setTargetDataSources()设置目标数据源。ARDS类实现了InitializingBean接口,重写了afterPropertySet()(对该方法不熟悉的话请回顾一下 Bean 的生命周期,该方法在 Bean 的属性注入后执行),该方法内部对 resolvedDataSources 属性赋值(将targetDataSources的值放进去),后续会用到 resolvedDataSources ; - ARDS 实现了 DataSource 接口,重写了
getConnection(),当 ORM 框架执行 sql 语句前总是执行 getConnection(),然后就调用到了重写后的 getConnection(),该方法内部调用了 ARDS 类的determineTargetDataSource(); - determineTargetDataSource() 内部调用了自定义类重写的
determineCurrentLookupKey(),返回数据源的映射,然后从 resolvedDataSources(map) 属性获取到数据源,进行后续的操作。
(题外话)想要实现数据源切换可以有两种实现:
- 手动切换数据源,每次执行相应操作前调用
router方法切换; - 还有一种思路就是利用 AOP,设计一个注解,注解内添加数据源唯一标识的属性,然后对方法添加注解,AOP 代码进行拦截,然后将唯一标识赋值给
ThreadLocal变量即可。
AbstractRoutingDataSource 实现动态数据源切换原理简单分析的更多相关文章
- SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换
SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换 本文转载至:http://exceptioneye.iteye.com/blog/1698064 Spri ...
- Spring主从数据库的配置和动态数据源切换原理
原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...
- AbstractRoutingDataSource实现动态数据源切换 专题
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换
转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...
- 利用AbstractRoutingDataSource实现动态数据源切换
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- spring AbstractRoutingDataSource实现动态数据源切换
使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...
- Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用
[参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...
- 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...
随机推荐
- 小师妹学JVM之:cache line对代码性能的影响
目录 简介 一个奇怪的现象 两个问题的答案 CPU cache line inc 和 add 总结 简介 读万卷书不如行万里路,讲了这么多assembly和JVM的原理与优化,今天我们来点不一样的实战 ...
- CentOS 7 的防火墙开启2
在虚拟机 CentOS 7 上装了 Nginx,结果发现另一台电脑无法访问其默认页面,通过 telnet 192.168.1.88 80 监听发现是 http 80 端口被 CentOS 7 的防火墙 ...
- PE文件结构详解(三)
0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构——区块表. 0x01 区块 区块就是PE载入器将PE ...
- Django---进阶6
目录 聚合查询 分组查询 F与Q查询 django中如何开启事务 orm中常用字段及参数 数据库查询优化 图书管理系统 作业 聚合查询 # 聚合查询 aggregate ""&qu ...
- js 整理 前端知识点 前端面试题 (2020)(vue)
数据类型 字符串(String).数字(Number).布尔(Boolean).对空(Null).未定义(Undefined).Symbol. 引用数据类型:对象(Object).数组(Array). ...
- day76 vue框架入门
目录 一.vue.js快速入门使用 1 vue.js库的下载 2 vue.js库的使用 3 vue.js的M-V-VM思想 4 显示数据 二.常用指令 1 操作属性 2 事件的绑定 3 样式操作 3. ...
- TB6560电机驱动器参数设置
TB6560电机驱动器参数设置 最近接触了一些步进电机的开发,整理了一些参数设置的经验,希望能帮助到有需要的人儿~ 步进电机主要按一定的给电规律,给对应的绕组响应的电信号,电机将按一定的方向运行,而且 ...
- 数据可视化之powerBI基础(六)Power BI的“问答”,你用过吗?
https://zhuanlan.zhihu.com/p/64412190 本文来自星球嘉宾"海艳"的分享,关于Power BI的问答功能以及各项细节,海艳详细为大家逐一介绍,下 ...
- 关于PowerShell调用Linq的一组实验
Windows PowerShell 版权所有 (C) Microsoft Corporation.保留所有权利. 尝试新的跨平台 PowerShell https://aka.ms/pscore6 ...
- Host是什么?如何设置host文件?
前言 前几天我在使用一些软件和网站时,出了一些小问题,然后我在网上搜解决问题的方法,搜着搜着就看到频繁出现的Host这个词.以前还没有注意到这个东西,因为总觉得它是系统文件,没必要去乱动:但是经过这次 ...