多数据源动态路由基于abstractroutingdatasource,通过线程上下文设置路由键,在determinecurrentlookupkey()中返回该键以匹配targetdatasources中的对应数据源,各数据源需独立配置hikaricp连接池,且事务应限定于单数据源内。

理解多数据源动态路由的核心逻辑
多数据源动态路由不是简单地配置多个 DataSource,而是让应用在运行时根据业务规则(如租户 ID、请求路径、方法注解、用户角色等)自动选择对应的数据源。连接池(如 HikariCP、Druid)本身不提供路由能力,它只是被路由逻辑“选中后”使用的底层资源。因此,关键在于:在 Spring 的 AbstractRoutingDataSource 上做文章,将多个已初始化的连接池作为目标数据源注入进去。
用 AbstractRoutingDataSource 实现动态切换
AbstractRoutingDataSource 是 Spring 提供的抽象类,它通过重写 determineCurrentLookupKey() 方法,返回一个 key,再由内部的 targetDataSources(Map
- 定义一个线程安全的上下文容器(如 InheritableThreadLocal
)存放当前请求的路由键(例如 tenant-a) - 在拦截器、AOP 切面或网关层提前设置该 key(比如从 HTTP Header、JWT 或方法参数中提取)
- 自定义子类继承 AbstractRoutingDataSource,在 determineCurrentLookupKey() 中返回该 key
- 将多个已配置好的 HikariCP 数据源(每个对应一个库)注册进 targetDataSources,key 与数据源一一对应
结合连接池配置多个物理数据源
每个真实数据库都需要独立的连接池实例。以 HikariCP 为例,不要共用同一个配置对象,而应为每个数据源单独构建 HikariDataSource:
- 用 @ConfigurationProperties(prefix = "spring.datasource.a") 加载 a 库的 JDBC 参数(url、username、password 等)
- 用 @Bean 方法创建并返回一个 HikariDataSource 实例,设置其 pool name、max pool size 等
- 同理创建 b 库、c 库的 HikariDataSource,并统一注册到路由数据源的 targetDataSources Map 中
- 注意:所有 HikariCP 实例都应被 Spring 容器管理,避免手动 new 导致无法监控或关闭
事务与动态路由的协同要点
动态路由和声明式事务(@Transactional)容易冲突——Spring 事务管理器默认绑定到单个数据源。若需跨数据源事务,JTA 不推荐(复杂且重),更常见的是按业务隔离:
- 确保一次请求只操作一个数据源(即路由 key 在请求生命周期内不变),这样 DataSourceTransactionManager 能正常工作
- 如果必须跨库更新,改用最终一致性方案(如发 MQ、本地消息表),而非强一致事务
- 避免在事务方法内修改路由上下文(如调用 setTenant("b")),否则事务可能绑定错数据源,导致静默失败
- 建议把路由决策放在事务边界之外,例如 Controller 层解析 tenant 后,再进入 @Transactional Service 方法
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











