spring事务@Transactional在同一个类中的方法调用不生效

时间:2024-03-14 07:39:53

参考:

  1. https://www.jianshu.com/p/2e4e1007edf2
  2. https://blog.csdn.net/ligeforrent/article/details/79996797

问题

也可以描述为 同一个类中,只有方法加了 @Transactional 事务为什么不生效?

  1. 类上加事务,方法不加事务,事务是生效的!
  2. 类上加事务,方法也加事务,事务是生效的!

  Spring提供了非常强大的事务管理机制,之前一直以为只要在想要加注解的方法上加@Transactional注解就万事大吉了
spring事务@Transactional在同一个类中的方法调用不生效
  但是今天发现这样做在某些情况下会有bug,导致事务无法生效。

  当这个方法被同一个类调用的时候,spring无法将这个方法加到事务管理中

  我们来看一下生效时候和不生效时候调用堆栈日志的对比。
spring事务@Transactional在同一个类中的方法调用不生效

  通过对比两个调用堆栈可以看出,spring的@Transactional事务生效的一个前提是进行方法调用前经过拦截器TransactionInterceptor,也就是说只有通过TransactionInterceptor拦截器的方法才会被加入到spring事务管理中,查看spring源码可以看到,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中会从调用方法中获取@Transactional注解,如果有该注解,则启用事务,否则不启用。
spring事务@Transactional在同一个类中的方法调用不生效

  这个方法是通过spring的AOP类CglibAopProxy的内部类DynamicAdvisedInterceptor调用的,而DynamicAdvisedInterceptor继承了MethodInterceptor,用于拦截方法调用,并从中获取调用链。

  Transactional是Spring提供的事务管理注解。
  重点在于,Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。
  而在同一个class中,方法B调用方法A,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用,也就无法通过注解保证事务性了。
   也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用。

解决方案:

  1. 可以将方法放入另一个类,并且该类通过spring注入,即符合了在对象之间调用的条件。
  2. 获取本对象的代理对象,再进行调用。具体操作如:
    1)Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
    2)在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调用事务方法,强行经过代理类,**事务切面。
  3. 很多时候,方法内调用又希望**事务,是由于同一个方法既有DAO操作又有I/O等耗时操作,不想让耗时的I/O造成事务的太长耗时(比如新增商品同时需要写入库存)。此时,可以将I/O做成异步操作(如加入线程池),而加入线程池的操作即便加入事务也不会导致事务太长,问题可以迎刃而解。???这条不太懂