泛型方法不处理事务传播或数据源切换,而是作为类型安全、可复用的调用入口,封装业务线、传播行为与数据源三层逻辑;通过transactionexecutor.executeincontext统一调度,配合tenantcontextholder和abstractroutingdatasource实现动态路由与事务控制。

泛型方法本身不直接处理事务传播或数据库连接切换——这是 Spring 事务管理器和数据源路由(如 AbstractRoutingDataSource)的职责。但你可以用泛型方法作为**类型安全、可复用的调用入口**,把“不同业务线 + 不同传播行为 + 不同数据源”这三层逻辑优雅地封装起来,避免重复模板代码。
泛型执行器:统一调度不同事务上下文
定义一个泛型方法,接收业务参数、传播行为、数据源标识,并返回强类型结果:
@FunctionalInterface
public interface TransactionalOperation
T execute();
}
public class TransactionExecutor {
// 泛型方法:自动绑定事务属性与数据源
public static
String tenantId, // 业务线标识,用于路由数据源
Propagation propagation,
TransactionalOperation
// 1. 绑定当前租户到 ThreadLocal(供 RoutingDataSource 读取)
TenantContextHolder.setTenantId(tenantId);
// 2. 动态构造 @Transactional 注解语义(实际需借助 AOP 或编程式事务)
TransactionTemplate template = buildTemplateFor(propagation, tenantId);
return template.execute(status -> operation.execute());
}
}
按业务线自动切换数据源(非泛型但必须配合)
泛型方法不负责切换数据源,但它是触发切换的“安全门”。关键在 AbstractRoutingDataSource 实现:
- 重写
determineCurrentLookupKey(),从TenantContextHolder.getTenantId()读取当前业务线 ID - 配置多个
targetDataSources,如tenant_a_ds、tenant_b_ds,key 与业务线 ID 对齐 - 确保每次调用
TransactionExecutor.executeInContext(...)前已设置租户,否则 fallback 到默认库
组合传播行为与业务线的典型用法
不再为每个业务写一堆重复的 @Transactional 方法,而是用泛型方法一行表达意图:
// 订单创建:需强一致性 → REQUIRED + 主库
Order order = TransactionExecutor.executeInContext(
"ecommerce", Propagation.REQUIRED,
() -> orderService.create(orderDto));
// 日志归档:允许失败不影响主流程 → REQUIRES_NEW + 归档库
TransactionExecutor.executeInContext(
"log_archive", Propagation.REQUIRES_NEW,
() -> logService.archiveOldRecords());
// 查询报表:只读 + 多租户从库 → SUPPORTS + report_ds
ReportData data = TransactionExecutor.executeInContext(
"reporting", Propagation.SUPPORTS,
() -> reportService.generateMonthlySummary());
为什么这样算“优雅”?
-
类型安全:泛型
<t></t>确保返回值无需强制转型,编译期捕获类型错误 - 关注分离:事务传播、数据源路由、业务逻辑三者解耦,各自可独立测试和替换
- 消除样板:不用为每个业务线+每种传播行为写 N×M 个带注解的方法
-
可扩展:新增业务线只需注册数据源 + 传入新 tenantId;新增传播策略可扩展
buildTemplateFor
本质不是“用泛型实现事务”,而是用泛型方法做干净的胶水层,把 Spring 的事务能力、多数据源能力、业务语义串成一条高内聚、低耦合的链路。










