分析spring事务@Transactional注解在同⼀个类中的⽅法之间调
⽤不⽣效的原因。。。
问题:
在Spring管理的项⽬中,⽅法A使⽤了Transactional注解,试图实现事务性。但当同⼀个class中的⽅法B调⽤⽅法A时,会发现⽅法A中的异常不再导致回滚,也即事务失效了。
当这个⽅法被同⼀个类调⽤的时候,spring⽆法将这个⽅法加到事务管理中。
我们来看⼀下⽣效时候和不⽣效时候调⽤堆栈⽇志的对⽐。
通过对⽐两个调⽤堆栈可以看出,spring的@Transactional事务⽣效的⼀个前提是进⾏⽅法调⽤前经过TransactionInterceptor,也就是说只有通过TransactionInterceptor的⽅法才会被加⼊到spring事务管理中,查看spring源码可以看到,在
这个⽅法是通过spring的AOP类CglibAopProxy的内部类DynamicAdvisedInterceptor调⽤的,⽽DynamicAdvisedInterceptor继承了MethodInterceptor,⽤于拦截⽅法调⽤,并从中获取调⽤链。
如果是在同⼀个类中的⽅法调⽤,则不会被⽅法拦截到,因此事务不会起作⽤,必须将⽅法放⼊另⼀个类,并且该类通过spring注⼊。
原因:
Transactional是Spring提供的事务管理注解。
重点在于,Spring采⽤动态代理(AOP)实现对bean的管理和切⽚,它为我们的每个class⽣成⼀个代理对象。只有在代理对象之间进⾏调⽤时,可以触发切⾯逻辑。
springframework事务⽽在同⼀个class中,⽅法B调⽤⽅法A,调⽤的是原对象的⽅法,⽽不通过代理对象。所以Spring⽆法切到这次调⽤,也就⽆法通过注解保证事务性了。
也就是说,在同⼀个类中的⽅法调⽤,则不会被⽅法拦截到,因此事务不会起作⽤。
解决⽅法1:
将事务⽅法放到另⼀个类中(或者单独开启⼀层,取名“事务层”)进⾏调⽤,即符合了在对象之间调⽤的条件。
解决⽅法2:
获取本对象的代理对象,再进⾏调⽤。具体操作如:
1) l上下⽂中,增加配置:<aop:aspectj-autoproxy expose-proxy="true"/>
2) 在xxxServiceImpl中,⽤(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调⽤事务⽅法,强⾏经过代理类,激活事务切⾯。
解决⽅法3:
很多时候,⽅法内调⽤⼜希望激活事务,是由于同⼀个⽅法既有DAO操作⼜有I/O等耗时操作,不想让耗时的I/O造成事务的太长耗时(⽐如新增商品同时需要写⼊库存)。此时,可以将I/O做成异步操作(如加⼊线程池),⽽加⼊线程池的操作即便加⼊事务也不会导致事务太长,问题可以迎刃⽽解。
解决⽅法4:
⽤@Autowired 注⼊⾃⼰然后在⽤注⼊的bean调⽤⾃⼰的⽅法也可以
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论