注解本身不直接实现分布式事务,仅作为声明式事务的“标记开关”,真正由框架(如spring、seata)通过aop拦截并织入事务逻辑;@transactional默认仅支持本地事务,跨服务需@globaltransactional等分布式注解配合上下文透传与协议协同。

注解本身不直接实现分布式事务,它只是声明式事务控制的“标记开关”。真正起作用的是框架(如 Spring、Seata)在运行时对这些注解的解析与增强——通过 AOP 拦截方法调用,自动织入事务管理逻辑(开启、提交、回滚)。关键在于:注解定义“要不要管”,框架决定“怎么管”。
常见事务注解及其语义边界
在 Spring 生态中,@Transactional 是最核心的声明式事务注解,但它默认只对本地数据库事务生效:
- 仅作用于单数据源、单服务内的 JDBC/MyBatis 操作,无法跨服务、跨库协调
- 异常类型决定是否回滚:默认只对 RuntimeException 及其子类 回滚;检查型异常(如 Exception)需显式配置
rollbackFor = Exception.class - 传播行为(
propagation)影响嵌套调用:例如REQUIRES_NEW会挂起当前事务、新建独立事务,适合日志记录等需隔离的操作 - 超时(
timeout)、只读(readOnly)、隔离级别(isolation)等属性均面向本地事务优化,对分布式场景无直接约束力
分布式事务中注解的“升级用法”
当引入 Seata、ShardingSphere-Transaction 等分布式事务框架后,原生 @Transactional 需配合特定注解才能激活全局协调能力:
- @GlobalTransactional(Seata):替代 @Transactional,标识整个方法为一个全局事务入口。协调者(TC)据此生成 XID,并在 RPC 调用链中透传,使下游服务能自动加入同一分布式事务
- @GlobalLock(Seata):用于需要读取未提交数据但又需防脏读的场景,在查询方法上加该注解可触发 AT 模式下的全局锁校验
- Feign 或 Dubbo 客户端调用需启用“事务上下文透传”:例如 Seata 中需配置
feign.hystrix.enabled=false(避免 Hystrix 线程隔离破坏 XID 传递),或使用@SentinelResource+ 自定义处理保证事务链路不中断
回滚策略如何通过注解联动生效
分布式回滚不是靠单个注解触发的,而是由注解声明 + 框架拦截 + 协议协同完成:
- 当 @GlobalTransactional 方法内抛出未被捕获的异常,Seata 的切面会捕获并通知 TC 发起两阶段回滚(2PC)
- 若某参与者在 Prepare 阶段失败(如库存服务扣减失败),TC 收到 “NO” 响应后,向所有已执行 Prepare 的参与者发送 Rollback 请求,各服务依据本地 undo_log 回滚
- 业务代码中可主动控制回滚点:例如在扣减库存前校验库存余量,不满足则 throw new RuntimeException("库存不足"),触发全局回滚
- 注意降级逻辑与事务边界冲突:@FallbackFactory 中的降级方法不应操作参与分布式事务的资源,否则可能造成状态不一致
实际开发中的典型误用与规避
很多问题源于混淆注解层级或忽略执行环境约束:
- 在非 public 方法上使用 @Transactional 或 @GlobalTransactional:Spring AOP 代理失效,事务不生效
- 异步线程(@Async、线程池)中调用带事务注解的方法:XID 无法自动继承,需手动绑定上下文(如 Seata 的
RootContext.bind(xid)) - 同一服务内多个本地事务方法被 @GlobalTransactional 包裹,但内部调用未走代理(this.method()):导致部分操作未纳入全局事务范围
- 未配置 Seata 的 DataSourceProxy 包装器:即使加了 @GlobalTransactional,SQL 也不会被解析生成 undo_log,回滚将失败










