SpringAOP自调用导致事务失效

SpringAOP自调用导致事务失效

若同一类中的其他没有 @Transactional 注解的方法内部调用有 @Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。

当一个没有 @Transactional 注解的方法在同一个类中调用一个有 @Transactional 注解的方法时,@Transactional 注解的事务管理将不起作用。这是因为事务管理是通过代理对象实现的,而在同一个类中的方法调用不会通过代理对象。这种情况下,事务管理将失效,导致方法不在事务范围内执行。

为了解决这个问题,你可以采用以下两种方法:
1、将具有 @Transactional 注解的方法移动到另一个独立的 Spring bean 中,然后通过注入这个 bean 的方式在当前类中调用其方法。这样调用会经过代理对象,事务管理将正常工作。

@Service
public class TransactionalService {
    @Transactional
    public void methodWithTransaction() {
        //...
    }
}

@Service
public class NonTransactionalService {
    
    @Autowired
    private TransactionalService transactionalService;

    public void methodWithoutTransaction() {
        // 调用带有 @Transactional 注解的方法
        transactionalService.methodWithTransaction();
    }
}

2、使用 AopContext.currentProxy() 显式地获取代理对象,这样你可以在同一个类中调用具有 @Transactional 注解的方法,同时保留事务管理功能。

@Service
public class MyService implements ApplicationContextAware {

    private ApplicationContext applicationContext;
    
    @Transactional
    public void methodWithTransaction() {
        //...
    }

    public void methodWithoutTransaction() {
        // 获取代理对象
        MyService proxy = (MyService) AopContext.currentProxy();
        // 通过代理对象调用带有 @Transactional 注解的方法
        proxy.methodWithTransaction();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

注意:为了使用 AopContext.currentProxy(),需要在 Spring 配置中启用 expose-proxy 属性:

<aop:config expose-proxy="true">
    
aop:config>

或者在 Java 配置中:

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

产生该问题的原因

当在 Spring 应用程序中使用@Transactional注解时,Spring 会为被注解的类创建一个代理对象。这个代理对象会根据方法上的@Transactional 注解来决定如何处理事务。

当你调用一个被@Transactional 注解修饰的方法时,代理对象会拦截这个方法调用,然后根据注解中的配置(例如传播行为、隔离级别等)来启动或加入一个事务。在事务处理完成后,代理对象会负责提交或回滚事务。

对于没有 @Transactional 注解的方法,代理对象会直接调用被代理对象的实际方法,不会涉及事务处理。这也意味着,如果你在同一个类中调用一个有 @Transactional 注解的方法,而调用者方法没有注解(也就是没有经过代理对象调用),那么 @Transactional 注解将不起作用。
注意: 当一个类中的方法使用了 @Transactional 注解,Spring 会在创建该类的实例时为其生成一个代理对象。具体而言,这个过程发生在以下时机:

1、容器启动时:当 Spring 容器启动时,它会扫描所有的 Bean 定义并创建相应的 Bean 实例。如果一个类中的方法带有 @Transactional 注解,Spring 容器会在创建这个类的实例时为其生成一个代理对象。

2、懒加载:如果你将 Bean 配置为懒加载(例如在 XML 配置中使用 lazy-init=“true” 或在 Java 配置中使用 @Lazy 注解),那么代理对象的创建将会在第一次请求这个 Bean 时进行。这意味着,在第一次调用该类的任何方法之前,Spring 容器会先创建一个代理对象。

代理对象的创建过程是透明的,这意味着从开发者的角度看,你将直接使用 Spring 容器中的 Bean,而不需要关心代理对象的创建和管理。Spring 容器会确保在需要时自动创建和使用代理对象。例如,当你通过依赖注入(如 @Autowired)获取一个带有 @Transactional 注解的 Bean 时,你实际上获取的是代理对象,而不是目标对象。

当自动注入带有@Transactional注解的方法时注入的其实是代理对象

总结

当一个类中的方法A(没有 @Transactional 注解)内部调用另一个方法B(有 @Transactional 注解),并且这两个方法都是同一个类的成员方法时,事务注解可能会失效。原因如下:

1 、当你调用方法A时,是调用的代理对象。然而,由于方法A上没有 @Transactional 注解,代理对象会直接调用目标对象的方法A。

2 、当执行到方法A里面发现要执行该类的方法B时,由于是在目标对象中进行调用的,所以又直接调用了目标对象中的方法B。而目标对象中的方法B是没有代理的,从而导致事务管理失效。

你可能感兴趣的:(java,spring,spring,boot)