接口在多租户系统中定义路由行为契约,统一租户上下文(TenantContext)、解耦数据操作与数据源归属(IDataSourceAware、IDao)、封装路由策略(TenantDataSourceRouter),确保物理隔离不被绕过。

接口在多租户系统中不是“参与路由”的执行者,而是定义“谁该路由、何时路由、如何识别目标”的行为契约。它把数据源切换这件事从业务代码里彻底摘出来,让上层只关心“我要操作什么数据”,不关心“这个数据在哪个库”。
用接口统一租户上下文的获取方式
定义如 TenantContext 这样的接口(或工具类契约),暴露 getTenantId()、setTenantId(String)、clear() 三个核心方法。所有租户识别入口——无论是拦截器、Filter 还是 RabbitMQ 消费端——都必须通过它存取租户 ID。这样业务层、路由层、AOP 校验层都依赖同一契约,避免各写一套 ThreadLocal 或上下文管理逻辑。
用泛型接口解耦数据操作与数据源归属
定义分层泛型接口,例如:
-
IDataSourceAware
:声明“该操作需感知数据源”,含 getDataSourceKey()方法,返回如"tenant-a"; -
IDao
:定义增删改查契约,但不绑定具体实现; - 具体租户 DAO 实现类(如
TenantADaoImpl)实现IDao<user></user>并继承IDataSourceAware<user></user>,其getDataSourceKey()固定返回"tenant-a"。
上层服务调用时只需面向 IDao<user></user> 编程,Spring 容器按租户 ID 自动注入对应实现——数据源归属由接口实现决定,而非 if-else 硬编码。
立即学习“Java免费学习笔记(深入)”;
用策略接口封装路由决策逻辑
定义 TenantDataSourceRouter 接口,声明 resolveDataSourceKey(String tenantId) 方法。不同实现可对应不同隔离模式:
-
SchemaBasedRouter返回"shared_db::tenant_a"(共库独 schema); -
DatabaseBasedRouter返回"ds-tenant-a"(独库); -
ReadReplicaRouter在租户 ID 后追加"-slave"(租户级读写分离)。
AbstractRoutingDataSource.determineCurrentLookupKey() 内部只调用该接口,不写判断逻辑。更换隔离策略时,只需换实现类,不改路由主干。
接口确保物理隔离不被绕过
真正保障物理隔离的,不是数据库配了多少个连接池,而是每次 SQL 执行前,是否 100% 经过接口约定的路径拿到正确的数据源 key。只要 TenantContext.getTenantId() 在请求入口设值、在出口 clear,只要 TenantDataSourceRouter.resolveDataSourceKey() 的返回值严格匹配 targetDataSources 中注册的 key,哪怕新增一百个租户,也不需要改一行业务 DAO 代码——因为业务代码根本不碰数据源。



















