胡雪

尚未配妥剑,转眼便江湖。 愿力尽千帆,归来仍少年。

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

参考:https://blog.csdn.net/qq_30336433/article/details/83338835

 

最近在开发项目中踩到一个坑,以此记录下来。以备后来人借鉴

 

1、相信使用spring开发的小伙伴对@Transaction这个注解应该不会陌生。

spring提供了非常强大的事务管理机制,之前一直以为只要在方法上加上@Transaction就万事大吉了

 

 

但是最近发现有些情况下 这个注解会失效。

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

 

下面我们来看看为什么会失效?

其实 spring的@Transactional事务生效的一个前提是方法调用前经过拦截器TransactionInterceptor , 也就是说只有通过TransactionInterceptor拦截器的方法才会被加入到Spring事务管理中,

查看spring源码可以知道,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中会调用方法中获取@Transactional注解,如果有该注解则启用事务,否则不启用注解

 

 

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

如果是在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用,必须将方法放入另外一个类中,并且该类通过spring注入

 

总结一下:Transactional是Spring提供的事务管理注解

spring 采用动态代理(AOP)实现对Bean的管理和切片,它为我们的每个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做成异步操作(如加入线程池),而加入线程池的操作即便加入事务也不会导致事务太长,问题可以迎刃而解。???这条不太懂

 

posted on 2019-06-21 14:29  胡雪  阅读(1046)  评论(1编辑  收藏  举报