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 】
相关推荐:
Laravel如何配置任务调度?(Cron Job示例)
如何快速搭建自助建站会员专属系统?
西安专业网站制作公司有哪些,陕西省建行官方网站?
Laravel Session怎么存储_Laravel Session驱动配置详解
,在苏州找工作,上哪个网站比较好?
Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用
如何在IIS中新建站点并解决端口绑定冲突?
如何快速上传建站程序避免常见错误?
Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理
Python3.6正式版新特性预览
Laravel如何实现本地化和多语言支持?(i18n教程)
Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
如何制作一个表白网站视频,关于勇敢表白的小标题?
如何正确选择百度移动适配建站域名?
图册素材网站设计制作软件,图册的导出方式有几种?
Laravel怎么在Blade中安全地输出原始HTML内容
Laravel如何生成API文档?(Swagger/OpenAPI教程)
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
米侠浏览器网页背景异常怎么办 米侠显示修复
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
如何快速搭建高效可靠的建站解决方案?
Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势
详解Oracle修改字段类型方法总结
如何在新浪SAE免费搭建个人博客?
微信小程序制作网站有哪些,微信小程序需要做网站吗?
如何自定义建站之星网站的导航菜单样式?
北京网站制作公司哪家好一点,北京租房网站有哪些?
Laravel如何使用模型观察者?(Observer代码示例)
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
魔毅自助建站系统:模板定制与SEO优化一键生成指南
Laravel Seeder填充数据教程_Laravel模型工厂Factory使用
北京网站制作的公司有哪些,北京白云观官方网站?
如何挑选高效建站主机与优质域名?
Laravel如何为API编写文档_Laravel API文档生成与维护方法
canvas 画布在主流浏览器中的尺寸限制详细介绍
如何获取上海专业网站定制建站电话?
Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】
Python面向对象测试方法_mock解析【教程】
如何快速配置高效服务器建站软件?
Laravel如何配置Horizon来管理队列?(安装和使用)
如何用PHP快速搭建高效网站?分步指南
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
电商网站制作价格怎么算,网上拍卖流程以及规则?
如何自定义建站之星模板颜色并下载新样式?
Laravel如何编写单元测试和功能测试?(PHPUnit示例)
活动邀请函制作网站有哪些,活动邀请函文案?
北京专业网站制作设计师招聘,北京白云观官方网站?
如何挑选最适合建站的高性能VPS主机?
Laravel如何与Pusher实现实时通信?(WebSocket示例)

