spring事务内部调用失效的根本原因是this调用绕过代理,导致aop拦截器未触发;事务方法必须为public,否则事务属性为空;异常未捕获时可能误判事务生效,实际仅共享外层上下文。

Spring 事务在内部方法调用时失效,根本原因在于没有经过代理对象。@Transactional 注解本身不直接开启事务,它依赖 Spring 的 AOP 代理机制在方法执行前后织入事务逻辑——而这个“前后”只对通过代理对象发起的调用生效。
代理机制决定了调用路径
Spring 启动时会为加了 @Transactional 的 Bean 创建代理(JDK 动态代理或 CGLIB)。外部代码调用该 Bean 的方法时,实际调用的是代理对象;代理检测到事务注解,就会先开启事务,再执行目标方法,最后按异常情况决定提交或回滚。
但同一类中用 this.methodB() 调用另一个加了 @Transactional 的方法时,JVM 直接执行当前实例的方法,绕过了代理层。事务拦截器压根没被触发,就像没加注解一样。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
事务方法必须是 public
即使解决了调用路径问题,如果事务方法不是 public,依然会失效。Spring AOP 默认只拦截 public 方法——在源码 AbstractFallbackTransactionAttributeSource 的 computeTransactionAttribute 中,非 public 方法会直接返回 null,事务属性为空,自然不生效。
异常未传播也会掩盖问题
内部调用虽绕过代理,但如果外层方法本身有事务(比如 outer() 加了 @Transactional),inner() 虽是 this 调用,仍运行在已有事务上下文中。此时若 inner() 抛出未捕获的 RuntimeException,整个事务仍会回滚,容易误以为“内部调用也生效了”。但严格来说,inner() 自身的事务配置(如 propagation、timeout)已被忽略,仅共享外层事务上下文。
常见修复方式
- 将事务方法拆到独立的 @Service 类中,通过 @Autowired 注入后调用
- 在当前类中注入自身 Bean(需加 @Lazy 避免循环依赖):@Autowired @Lazy private UserService self;,然后用 self.methodB()
- 启用暴露代理并使用 AopContext.currentProxy():@EnableAspectJAutoProxy(exposeProxy = true),再写
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










