核心是运行时根据业务规则动态路由,依托abstractroutingdatasource的determinecurrentlookupkey方法结合threadlocal管理线程级数据源key,并通过aop+自定义注解实现透明切换,事务需绑定对应platformtransactionmanager且不可跨库。

理解多数据源动态切换的核心思路
Spring 中实现多数据源动态切换,关键不是“代理”本身,而是在线程执行时能根据业务规则(如注解、参数、上下文)决定使用哪个 DataSource。Spring 的 AbstractRoutingDataSource 是官方推荐的轻量级路由机制,它在每次获取连接前调用 determineCurrentLookupKey() 方法,返回一个 key,再从配置好的 targetDataSources 映射中查出对应的数据源。这本质上是运行时的“动态代理式路由”,无需手动写 JDK/CGLIB 代理。
基于 AbstractRoutingDataSource 实现路由
你需要继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey(),并配合 ThreadLocal 管理当前数据源标识:
- 定义一个 DataSourceKey 枚举或字符串常量,如
MASTER、SLAVE1、SLAVE2 - 用 ThreadLocal
存储当前线程绑定的数据源 key,提供 set/clear/get 方法 - 在
determineCurrentLookupKey()中返回DataSourceContextHolder.get(),若为空则默认返回 MASTER - 在配置类中将多个实际数据源(如 HikariCP 实例)注入到
targetDataSourcesMap,并设置defaultTargetDataSource
通过自定义注解 + AOP 自动切换数据源
避免在业务代码中频繁调用 DataSourceContextHolder.set(),可用 AOP 在方法执行前自动识别并切换:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义注解
@TargetDataSource(key = "SLAVE1"),支持类/方法级别 - 编写切面:拦截带该注解的方法,在
ProceedingJoinPoint执行前调用DataSourceContextHolder.set(key),执行后clear()(必须清理,防止线程复用导致污染) - 注意:若方法内调用其他本类方法(非 Spring 代理方法),AOP 不生效,需确保调用走的是代理对象(如
this.getClass().getDeclaredMethod(...).invoke(...)或注入自身 Bean)
事务与数据源切换的注意事项
Spring 的 @Transactional 默认绑定到单个 DataSource。多数据源下事务无法跨库,必须明确指定事务管理器:
- 为每个数据源配置独立的 PlatformTransactionManager(如
masterTxManager、slaveTxManager) - 在需要事务的方法上显式声明
@Transactional("masterTxManager") - 切记:同一事务内不能切换数据源,否则事务上下文会丢失;
@TargetDataSource注解应避开事务方法,或确保其仅用于只读查询 - 如果必须读写分离且强一致性,考虑应用层协调(如先写主库,再同步查从库)或使用分布式事务框架(Seata),但已超出动态数据源范畴
不复杂但容易忽略
动态数据源本质是“一次请求、一个线程、一个数据源 key”的契约。只要保证 ThreadLocal 正确设值、及时清理、AOP 切入点准确、事务管理器对齐,就能稳定工作。不需要引入额外代理库,Spring 原生机制足够支撑常见读写分离和分库场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










