spring boot 多数据源切换需基于 abstractroutingdatasource + threadlocal + aop 实现,通过 @datasource 注解与切面动态设置数据源键,确保事务内数据源一致、异步场景显式传递上下文。

在 Spring Boot 中配置多数据源切换,核心是用 AbstractRoutingDataSource 做路由中枢,配合 ThreadLocal 维护当前线程的数据源标识,再通过 AOP 或注解驱动切换逻辑。不写死、不手动 new、不侵入业务代码,才是可持续的方案。
数据源配置要分层管理
避免在配置类里硬编码每个 DataSource Bean。推荐结构:
-
基础层:用
@ConfigurationProperties绑定 yml 中的多个数据源配置(如spring.datasource.db1、spring.datasource.db2) -
实例层:用
DruidDataSourceBuilder或HikariDataSource创建具体数据源对象,统一加监控或过滤器 -
路由层:把所有数据源注入
DynamicDataSource的targetDataSourcesMap,并设好默认数据源
动态路由必须重写 determineCurrentLookupKey
继承 AbstractRoutingDataSource,关键只有一行:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.get(); // 从 ThreadLocal 取 key
}
这个 key 必须和你配置中定义的 map key 一致(比如 "master" 或 "tenant_001")。注意:resolvedDataSources 在容器启动后就固定了,运行时不能新增数据源——除非你重写 afterPropertiesSet 或用支持热注册的封装(如 dynamic-datasource)。
切换动作要靠上下文 + 切面来触发
单纯配好路由还不够,得让业务执行前“告诉”系统该用哪个库:
- 定义一个
@DataSource("slave")自定义注解 - 写一个 AOP 切面,在
@Around中拦截该注解,调用DataSourceContextHolder.set("slave") - 切面顺序很重要:必须在
@Transactional之前执行,否则事务已绑定连接,切换无效 - 方法结束后记得
remove(),防止线程复用导致污染(尤其在 Tomcat 线程池里)
事务和异步场景要特别小心
Spring 事务基于 Connection 绑定到 TransactionSynchronizationManager,一旦开启就不能换库。所以:
- 带
@Transactional的方法,整个事务内只能走同一个数据源;跨库操作需拆成多个事务,或改用最终一致性 - 用了
@Async或手动开新线程,ThreadLocal不会自动传递,必须显式复制上下文(例如用InheritableThreadLocal或TransmittableThreadLocal) - WebFlux 或响应式编程不适用这套 ThreadLocal 模式,得换用
Mono.subscriberContext()等机制
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










