Spring动态数据源实现读写分离详解
发布时间 - 2026-01-11 02:17:54 点击率:次一、创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性
package com.bounter.mybatis.extension;
/**
* 基于ThreadLocal实现的动态数据源容器,保证DynamicDataSource的线程安全性
* @author simon
*
*/
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();
public static void setDataSource(String dataSourceKey) {
dataSourceHolder.set(dataSourceKey);
}
public static String getDataSource() {
return dataSourceHolder.get();
}
public static void clearDataSource() {
dataSourceHolder.remove();
}
}
二、定义Spring动态数据源扩展类,用来实现Master、Slave数据源动态切换
package com.bounter.mybatis.extension;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 自定义的Spring 动态数据源扩展类,用来实现Master、Slave数据源动态切换
* @author simon
*
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
//使用DynamicDataSourceHolder保证线程安全
return DynamicDataSourceHolder.getDataSource();
}
}
三、配置Master、Slave数据源
1. db.properties配置Master、Slave数据信息
# Master DB db.master.url=jdbc:mysql://192.168.168.110:3306/bounter?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=PRC&useSSL=false db.master.username=bounter # AES encrypt,Base64 encode db.master.password=ZNhnEjauk3pecZxxS84ofA== # Slave DB db.slave.url=jdbc:mysql://192.168.168.111:3306/database?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=PRC&useSSL=false db.slave.username=bounter # AES encrypt,Base64 encode db.slave.password=jFYmt2f57RHhzItYDhWiSA==
2. Spring 配置文件配置Master、Slave连接池,动态数据源
<!-- Master数据源 -->
<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${db.master.url}" />
<property name="username" value="${db.master.username}" />
<property name="password" value="${db.master.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="20" />
<property name="minIdle" value="1" />
<property name="maxActive" value="40" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
</bean>
<!-- Slave数据源 -->
<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${db.slave.url}" />
<property name="username" value="${db.slave.username}" />
<property name="password" value="${db.slave.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="20" />
<property name="minIdle" value="1" />
<property name="maxActive" value="40" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
</bean>
<!-- 自定义动态数据源 -->
<bean id="dataSource" class="com.bounter.mybatis.extension.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 配置读写数据源 -->
<entry value-ref="masterDataSource" key="write"></entry>
<entry value-ref="slaveDataSource" key="read"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"></property>
</bean>
四、创建数据源切面,通过AOP实现根据Dao层方法前缀动态选取读、写数据源
package com.bounter.mybatis.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import com.bounter.mybatis.extension.DynamicDataSourceHolder;
/**
* 数据源切面,通过dao方法前缀决定访问读、写数据源
* @author simon
*
*/
@Component
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect {
//读库数据源key
private static final String DATASOURCE_KEY_READ = "read";
//查询方法清单
String[] queryMethods = {"find","get","query","count","select"};
/**
* dao层方法执行前选择数据源
* @param point
*/
@Before("execution(* com.bounter.mybatis.dao..*.*(..))")
public void before(JoinPoint point) {
// 获取到当前执行的方法名
String methodName = point.getSignature().getName();
//匹配查询方法
for(String queryMethod : queryMethods) {
if(methodName.startsWith(queryMethod)) {
//查询方法设置数据源为读库
DynamicDataSourceHolder.setDataSource(DATASOURCE_KEY_READ);
break;
}
}
}
/**
* dao层方法执行完后清空数据源选择
* @param point
*/
@After("execution(* com.bounter.mybatis.dao..*.*(..))")
public void after(JoinPoint point) {
DynamicDataSourceHolder.clearDataSource();
}
}
github源码地址:https://github.com/13babybear/bounter-mybatis
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Spring
# 数据源
# 读写分离
# 详解Nginx服务器中HTTP Headers相关的模块配置使用
# Spring配置动态数据源实现读写分离的方法
# Mybatis注解实现多数据源读写分离详解
# resty更新header控制api版本数据源读写分离
# 自定义
# 池中
# 多久
# 完后
# 配置文件
# 大家多多
# 清空
# 连接池
# mysql
# url
# DB
# master
# useUnicode
# true
# Object
# determineCurrentLookupKey
# protected
# extends
# Override
# properties
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
node.js报错:Cannot find module 'ejs'的解决办法
HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】
Angular 表单中正确绑定输入值以确保提交与验证正常工作
如何生成腾讯云建站专用兑换码?
android nfc常用标签读取总结
如何快速生成橙子建站落地页链接?
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】
如何在阿里云虚拟主机上快速搭建个人网站?
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲
Java遍历集合的三种方式
Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试
如何在IIS中新建站点并解决端口绑定冲突?
Laravel如何实现API速率限制?(Rate Limiting教程)
Laravel如何发送系统通知?(Notification渠道示例)
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
在线制作视频网站免费,都有哪些好的动漫网站?
Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理
黑客如何利用漏洞与弱口令入侵网站服务器?
免费网站制作appp,免费制作app哪个平台好?
浅谈Javascript中的Label语句
Laravel如何实现模型的全局作用域?(Global Scope示例)
如何在Windows环境下新建FTP站点并设置权限?
Python高阶函数应用_函数作为参数说明【指导】
如何快速登录WAP自助建站平台?
EditPlus中的正则表达式 实战(2)
C++时间戳转换成日期时间的步骤和示例代码
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】
如何快速搭建高效服务器建站系统?
中山网站制作网页,中山新生登记系统登记流程?
使用C语言编写圣诞表白程序
如何在阿里云ECS服务器部署织梦CMS网站?
如何在Windows服务器上快速搭建网站?
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
历史网站制作软件,华为如何找回被删除的网站?
非常酷的网站设计制作软件,酷培ai教育官方网站?
bing浏览器学术搜索入口_bing学术文献检索地址
如何用AI帮你把自己的生活经历写成一个有趣的故事?
如何制作一个表白网站视频,关于勇敢表白的小标题?
php结合redis实现高并发下的抢购、秒杀功能的实例
javascript中闭包概念与用法深入理解
Laravel怎么自定义错误页面_Laravel修改404和500页面模板
简历没回改:利用AI润色让你的文字更专业
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
Laravel如何使用withoutEvents方法临时禁用模型事件
如何在自有机房高效搭建专业网站?
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
如何在腾讯云服务器上快速搭建个人网站?

