[Spring]事务失效之同一类内的方法调用

在 Spring 中,事务是通过 AOP(面向切面编程)机制实现的。Spring 事务的管理是基于代理对象的,也就是说,Spring 会创建一个代理对象来拦截带有事务注解(如 @Transactional)的方法调用,并在方法执行前后进行事务的处理。因此,当某些情况下事务失效时,通常与 Spring 的代理机制有关。

具体来说,在同一类中的方法调用会导致 Spring 事务失效 的原因是 Spring 的代理机制无法拦截内部的自我调用(self-invocation),这破坏了事务的正确处理。以下是详细的解释:

1. Spring 事务的工作原理#

Spring 使用两种代理机制来管理事务:

  • JDK 动态代理:针对实现了接口的类,Spring 会创建一个实现了相同接口的代理类。事务逻辑通过代理类在方法调用时插入。
  • CGLIB 代理:针对没有实现接口的类,Spring 会使用 CGLIB 生成子类代理,拦截方法调用并插入事务逻辑。

不论哪种代理方式,Spring 都是在代理类中对事务进行管理。如果调用来自外部的类,代理对象会拦截该调用并正确地管理事务逻辑。

2. 为什么同类中的方法调用导致事务失效#

当一个类中的一个方法调用同类中的另一个方法时,如果调用的方法带有 @Transactional 注解,但这个调用是 直接的内部方法调用(不是通过代理对象调用的),Spring 事务将不会生效。这是因为内部方法调用不会通过 Spring 生成的代理类进行调用,而是直接在当前对象中执行,因此 Spring 无法介入处理事务。

例子:#

@Service
public class TransactionService {

    @Transactional
    public void publicMethod() {
        // 事务在这里生效
        internalMethod();
    }

    @Transactional
    public void internalMethod() {
        // 事务在这里不会生效,因为它是通过内部调用触发的
    }
}

在这个例子中,当 publicMethod() 被外部调用时,Spring 事务管理器能够生效。然而,publicMethod() 内部直接调用了 internalMethod(),这属于类内部的自我调用。由于 internalMethod() 没有通过代理类进行调用,Spring 事务管理器无法对这个方法进行拦截和处理,导致事务失效。

3. 事务失效的原因总结#

  • 内部调用:当类内部的方法调用另一个带有 @Transactional 注解的方法时,这个调用不会通过 Spring 的代理对象进行,而是直接通过 this 引用,因此 Spring 无法拦截并应用事务。
  • 代理对象失效:Spring AOP 代理机制只能拦截通过代理对象进行的方法调用,而不能拦截类内部的直接方法调用。

4. 如何解决同类中事务失效的问题#

为了解决类内部方法调用导致的事务失效问题,可以考虑以下几种解决方案:

方法 1:通过 Spring 容器重新获取代理对象#

在类内部通过 Spring 容器获取当前对象的代理实例,然后通过代理对象调用目标方法,从而让事务生效。

@Service
public class TransactionService {

    @Autowired
    private ApplicationContext context;

    @Transactional
    public void publicMethod() {
        // 从 Spring 容器中获取代理对象
        TransactionService proxy = context.getBean(TransactionService.class);
        proxy.internalMethod(); // 通过代理对象调用方法,事务生效
    }

    @Transactional
    public void internalMethod() {
        // 事务在这里生效
    }
}

方法 2:将事务性方法抽取到另一个类中#

将需要事务管理的方法放在另一个 Spring 管理的类中,然后通过依赖注入的方式调用,这样每次调用都是通过代理对象进行的,事务管理可以生效。

@Service
public class TransactionService {

    @Autowired
    private AnotherService anotherService;

    public void publicMethod() {
        // 调用另一个类中的方法,事务生效
        anotherService.internalMethod();
    }
}

@Service
public class AnotherService {

    @Transactional
    public void internalMethod() {
        // 事务在这里生效
    }
}

方法 3:使用 AOP 编程手动管理事务#

如果不想改变方法结构,也可以使用 Spring AOP 手动管理事务,显式地在方法内部控制事务的开始和结束。

@Service
public class TransactionService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void publicMethod() {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            internalMethod();
            transactionManager.commit(status);
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ex;
        }
    }

    @Transactional
    public void internalMethod() {
        // 事务生效
    }
}

方法 4:使用 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)#

使用 @EnableAspectJAutoProxy 并设置 exposeProxy = true,可以在内部调用时获取当前的代理对象,并通过代理对象调用方法。

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {}

@Service
public class TransactionService {

    @Transactional
    public void publicMethod() {
        // 获取代理对象
        TransactionService proxy = (TransactionService) AopContext.currentProxy();
        proxy.internalMethod(); // 通过代理对象调用方法
    }

    @Transactional
    public void internalMethod() {
        // 事务生效
    }
}

5. 总结#

  • 原因:Spring 事务通过代理机制实现,当类中的一个方法调用同类中另一个带有 @Transactional 注解的方法时,由于这个调用没有经过代理对象,而是直接调用自身,Spring 无法拦截并应用事务,因此事务失效。
  • 解决方案:可以通过获取代理对象、将事务性方法移到其他类中,或者显式地管理事务来解决类内部调用导致的事务失效问题。

正确理解 Spring 的代理机制和事务管理原理,可以避免事务失效的问题并有效管理事务。

作者:Esofar

出处:https://www.cnblogs.com/DCFV/p/18440588

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Duancf  阅读(1275)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示