Spring AOP之同一个对象方法内部自调用导致事务失效问题,自调用失效,解决,方法

探讨SpringAOP中同一对象方法自调用导致的事务失效问题,分析其原理,并提供三种解决方案,包括使用SpringUtil.getBean、AopContext.currentProxy()及自我注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring AOP之同一个对象方法内部自调用导致事务失效问题

 

 

对于像我这种喜欢滥用AOP的程序员,遇到坑也是习惯了,不仅仅是事务,其实只要脱离了Spring容器管理的所有对象,对于SpringAOP的注解都会失效,因为他们不是Spring容器的代理类,SpringAOP,就切入不了

当然可以使用原生ASPECTJ,不用SpringAOP,但是基于生态链问题,还是尽量使用SpringAOP

这里简单说一下,Spring如何选择使用CGLIB,或者是JDK代理,

简单来说,如果实现了某个接口,那么Spring就选择JDK代理(不一定),如果没有,那么就选择CGLIB代理,说起来简单,Spring还会闹脾气,不代理呢

 

问题引出

一直以来比较多的情况是在Controller 调用Service 的方法,把事务直接在Service的方法上,妥妥的没问题,事务正常执行

考虑以下问题: 同对象的方法B 调用自己的方法A,这里的事务将会失效(严格上来说,只要对方法A使用注解AOP均会失效),原因是因为这里的this.调用的并不是Spring的代理对象

@Service
public class ClassA{

        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void methodA(){

        }

        /**
         * 这里调用methodA() 的事务将会失效
         */
        public void methodB(){
            this.methodA();
        }

    }

解决方法一

最简单的解决方法为:(代理模式为JDK 的情况下(根据接口代理))

 @Service
    public class ClassA extends BaseClass{

        @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
        public void methodA(){

        }

        /**
         * 这里调用methodA() 的事务将会失效
         */
    
        public void methodB(){
        //使用getBean
    ((BaseClass)SpringUtil.getBean("classA")).methodA();
        }

    }

解决方法二

解决第一步: 必须

SpringBoot:注解开启cglib代理,开启 exposeProxy = true,暴露代理对象 (否则AopContext.currentProxy()) 会抛出异常

@EnableAspectJAutoProxy(exposeProxy = true)
public class Application{
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

配置方式:配置方式下,我用的是SpringBoot 1.5.4没有找到exposeProxy的选项,所以建议使用注解式.

传统Spring配置文件

XML

<aop:aspectj-autoproxy expose-proxy="true"/>

第二步(需要保证Spring对这个bean创建了代理对象,基本上涉及到Aop的方法的类,都会创建代理对象) 可以用以下代码判断

/**
 * Created by laizhenwei on 19:37 2017-10-14
 */
@Service("classImplProxy")
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class ClassImplProxy implements IClass {

    @Override
    @Transactional
    public void sysout() {
    }

    //是否代理对象
    @Override
    public boolean isAopProxy() {
        return AopUtils.isAopProxy(AopContext.currentProxy());
    }

    //是否cglib 代理对象
    @Override
    public boolean isCglibProxy() {
        return AopUtils.isCglibProxy(AopContext.currentProxy());
    }

    //是否jdk动态代理
    @Override
    public boolean isJdkDynamicProxy() {
        return AopUtils.isJdkDynamicProxy(AopContext.currentProxy());
    }
}

获取代理对象

  @Service
    public class ClassA{

        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void methodA(){

        }

        public void methodB(){
            //手动获取此对象Spring的代理类
            ((ClassA)AopContext.currentProxy()).methodA();

        }

    }

解决方法三

直接在当前类@Autowire 或者@Resource 注入自己,然后用注入的bean 调用方法

<think>好的,我现在要解决用户的问题:在Spring中,两个使用事务注解的方法内部调用时,事务失效问题如何解决。 首先,我需要回忆一下Spring事务的基本原理。Spring事务管理是通过AOP实现的,也就是基于代理的。当我们在一个类的方法上使用@Transactional注解时,Spring会为该类生成一个代理对象。在调用被代理对象方法时,会先经过代理对象的增强处理,比如开启事务、提交或回滚事务等。但是,如果在一个类的方法内部直接调用另一个带有@Transactional注解的方法,这并不会经过代理对象,因此事务注解不会生效,因为此时调用的是目标对象方法,而不是代理对象方法。 举个例子,假设有一个类中的方法A调用方法B,而方法B上有@Transactional注解。由于方法A和方法B在同一个类中,方法A直接调用方法B时,绕过了代理,导致事务没有开启。这就是问题所在。 接下来,我需要思考如何解决这个问题。常见的解决办法有几种: 1. **将方法B移到另一个类中**:通过将方法B放在另一个Bean中,然后在方法A中注入这个Bean并调用方法B。这样调用就会经过代理,事务就能生效。 2. **使用AopContext.currentProxy()**:在方法A中获取当前的代理对象,然后通过代理对象调用方法B。但这种方法需要配置expose-proxy,并且在代码中显式调用,可能不够优雅。 3. **通过ApplicationContext获取代理Bean**:在类中注入ApplicationContext,然后通过getBean获取自身的代理实例,再调用方法B。不过这种方法同样需要额外的代码。 4. **使用编程式事务管理**:在方法内部使用TransactionTemplate来手动管理事务,这样可以避免依赖代理机制。 现在需要详细解释每种方法的实现步骤,并给出示例代码。同时,需要指出为什么内部调用导致事务失效的原因,以及每种解决方法的适用场景。 例如,第一种方法的代码示例可能如下: ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; public void methodA() { // 其他业务逻辑 serviceB.methodB(); } } @Service public class ServiceB { @Transactional public void methodB() { // 需要事务的业务逻辑 } } ``` 这样,当methodA调用serviceB.methodB()时,会通过代理对象事务生效。 另一种方法是使用AopContext,但需要配置@EnableAspectJAutoProxy(exposeProxy = true),然后在代码中: ```java @Service public class SomeService { @Transactional public void methodA() { ((SomeService) AopContext.currentProxy()).methodB(); } @Transactional public void methodB() { // 业务逻辑 } } ``` 需要注意的是,这种方法需要确保AOP配置正确,并且可能带来一些性能上的开销。 最后,用户可能需要了解这些方法之间的优缺点,比如代码侵入性、维护成本等。此外,可能还需要考虑其他因素,比如事务传播行为是否正确设置,是否存在其他导致事务失效的情况(比如异常未被正确抛出等)。 在给出相关问题的时候,需要围绕事务失效的其他原因、不同解决方法的比较、Spring事务的传播机制等方面展开。</think>在Spring中,当同一个类中的方法A调用另一个带有`@Transactional`注解的方法B时,事务失效。这是因为Spring事务管理基于AOP代理机制,内部方法调用不会经过代理对象导致事务拦截器未被触发。以下是解决方案和原因解释: --- ### **问题原因** 1. **代理机制限制** Spring通过动态代理(JDK或CGLIB)实现事务管理。当方法A直接调用方法B时,实际上是目标对象内部调用,绕过了代理对象导致事务注解失效。 2. **AOP拦截范围** AOP只能拦截从外部类调用方法内部调用属于类自身行为,无法被代理拦截。 --- ### **解决方案** #### 1. **将方法抽取到另一个类** **原理**:通过将方法B移到另一个Bean中,强制从代理对象调用方法B。 **代码示例**: ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; public void methodA() { // 业务逻辑 serviceB.methodB(); // 通过代理调用 } } @Service public class ServiceB { @Transactional public void methodB() { // 需要事务的业务逻辑 } } ``` #### 2. **使用`AopContext.currentProxy()`** **原理**:通过`AopContext`获取当前代理对象,再调用方法B。 **步骤**: 1. 启用AOP代理暴露:在配置类添加`@EnableAspectJAutoProxy(exposeProxy = true)`。 2. 在方法A中通过代理调用方法B。 **代码示例**: ```java @Service public class SomeService { public void methodA() { ((SomeService) AopContext.currentProxy()).methodB(); } @Transactional public void methodB() { // 业务逻辑 } } ``` #### 3. **注入自身代理(不推荐)** **原理**:通过`ApplicationContext`获取代理对象调用方法B。 **代码示例**: ```java @Service public class SomeService implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext context) { this.context = context; } public void methodA() { SomeService proxy = context.getBean(SomeService.class); proxy.methodB(); } @Transactional public void methodB() { // 业务逻辑 } } ``` #### 4. **编程式事务管理(手动控制)** **原理**:使用`TransactionTemplate`手动管理事务。 **代码示例**: ```java @Service public class SomeService { @Autowired private TransactionTemplate transactionTemplate; public void methodA() { transactionTemplate.execute(status -> { methodB(); return null; }); } public void methodB() { // 业务逻辑 } } ``` --- ### **总结** - **推荐方案**:优先选择将方法拆分到不同类中(方案1),代码侵入性低且符合设计原则。 - **慎用方案**:`AopContext`和自身代理注入(方案2、3)需修改原有代码,可能引入循环依赖问题。 - **灵活方案**:编程式事务(方案4)适合需要精细控制事务的场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值