
Spring 的 @Transactional 注解依赖 AOP 代理机制生效,当事务方法被同一类中的其他方法直接调用时,会绕过代理对象,导致事务注解失效。
spring 的 `@transactional` 注解依赖 aop 代理机制生效,当事务方法被同一类中的其他方法直接调用时,会绕过代理对象,导致事务注解失效。
在你提供的代码中,transactionalError() 方法虽标注了 @Transactional,但它被同属 Test 类的 add() 方法(非事务方法)直接调用——这属于 self-invocation(自调用) 场景。此时 JVM 调用的是目标对象的原始方法,而非 Spring 生成的代理对象,因此事务切面(如事务开启、回滚逻辑)根本不会执行,"".substring(2) 抛出的 StringIndexOutOfBoundsException 无法触发事务回滚,数据仍会持久化到数据库。
而 add2() 方法本身加了 @Transactional,其内部调用 transactionalError() 虽仍是同对象调用,但由于整个 add2() 执行已在事务上下文中(由代理拦截并开启事务),且 transactionalError() 是普通方法调用(无事务传播要求),所以最终整个操作处于一个有效事务中;异常抛出后,Spring 事务管理器检测到未捕获的运行时异常,执行回滚——表现正常。
✅ 正确做法是将 @Transactional 方法移至独立的 Service 类中,确保调用方通过 Spring 容器注入的代理对象访问:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
@Service
public class TransactionalService {
@Transactional
public void transactionalError(IMyService myService) {
MyObj myObj = new MyObj();
myService.save(myObj);
"".substring(2); // 触发 RuntimeException
MyObj myObj2 = new MyObj();
myService.save(myObj2);
}
}
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class Test {
private final IMyService myService;
private final TransactionalService transactionalService; // 注入代理Bean
@GetMapping("/1")
public void add() {
transactionalService.transactionalError(myService); // ✅ 经由代理调用
}
}⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
- @Transactional 默认仅对 未检查异常(RuntimeException 及其子类)和 Error 回滚;若需对受检异常回滚,需显式声明 @Transactional(rollbackFor = Exception.class);
- 确保目标类由 Spring 容器管理(使用 @Service/@Component 等),且调用方通过 @Autowired 或构造器注入,而非 new 实例化;
- 同一类内调用永远无法触发事务(无论是否 public),这是 Spring AOP 代理机制的根本限制,非配置或版本问题。
总结:事务不是“方法级魔法”,而是“代理拦截+上下文传播”的协作结果。将事务逻辑拆分到独立 Bean,是保障事务可靠性的最小实践原则。

















