java注解本身不实现多租户隔离,需结合aop、动态数据源或sql拦截等机制运行时生效;自定义注解如@tenantscoped标记类/方法,配合threadlocal传递租户上下文,并在持久层自动注入tenant_id条件。

Java 中注解本身不直接实现多租户数据隔离,它只是声明式元数据;真正起作用的是结合 AOP、动态数据源、SQL 拦截(如 MyBatis 插件)或 JPA 实体监听等机制,在运行时根据注解标记自动注入租户上下文或改写查询逻辑。
用自定义注解标记租户敏感的类或方法
定义如 @TenantScoped 或 @TenantFilter 注解,用于标识需要做租户隔离的 Service 方法、Repository 接口或实体类:
- 注解可设为
@Target({TYPE, METHOD}),支持类级(全局租户字段)和方法级(临时切换) - 加上
@Retention(RetentionPolicy.RUNTIME),确保运行时可通过反射读取 - 可选添加属性如
value() default "",用于指定租户 ID 来源(如 header、token、参数名)
结合 ThreadLocal + 拦截器传递租户上下文
在 Web 层(如 Spring MVC 的 HandlerInterceptor 或 WebFilter)中解析请求中的租户标识(如请求头 X-Tenant-ID),并存入 TenantContext(基于 ThreadLocal 的静态容器):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 拦截器中调用
TenantContext.setTenantId(tenantId) - Service/DAO 层通过
TenantContext.getTenantId()获取当前租户 - 注意在线程池场景下需手动传递(如使用
TransmittableThreadLocal)
在持久层自动注入租户条件
根据注解和上下文,对 SQL 查询/更新做透明增强:
-
MyBatis:用 Interceptor 拦截 StatementHandler,扫描 Mapper 方法是否被
@TenantScoped标记,若命中则在 WHERE 条件末尾追加AND tenant_id = ?,并绑定参数 -
JPA/Hibernate:用 @EntityListeners 或 Hibernate Filter,定义
@Filter(name = "tenantFilter", condition = "tenant_id = :tenantId"),并在启用 Filter 时设置参数:tenantId为当前上下文值 -
Spring Data JPA:重写 Query Lookup Strategy,或统一在 Repository 方法名中约定(如
findByxxxAndTenantId),再配合 AOP 自动补全 tenantId 参数
避免硬编码,让注解驱动行为而非配置
不推荐在每个 DAO 方法里手动拼 tenant_id = ?,而是通过注解触发统一拦截逻辑:
- 例如给某个 Service 方法加
@TenantScoped("header"),表示从请求头提取租户 ID 并激活租户过滤 - 实体类上加
@TenantAware,表示该实体所有增删改查都必须校验租户上下文,为空则抛异常 - 配合 Spring 的
@Aspect切面,在目标方法执行前校验TenantContext是否就绪,未设置则拒绝执行
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










