问题:
在Spring管理项目中,方法A使用Transactional注释,试图实现事务性。但是,当同一class中的方法B调用方法A时,会发现方法A中的异常不再导致回滚,即事务失效。
当该方法被同一类调用时,spring无法将该方法添加到事务管理中。
让我们来看看堆栈日志在生效和不生效时的比较。
通过比较两个调用堆栈,我们可以看到spring@transactional事务生效的前提之一是在调用方法之前通过拦截器transactioninterceptor,也就是说,只有通过transactioninterceptor 将LJQ添加到spring事务管理中,查看spring源码即可看到,在AdvisedSuport中.getinterceptorsandynamicinterceptionadvice方法将从调用方法中获得@transactional注释。如果有注释,则启用事务,否则不启用。
这种方法是通过SpringAOP类Cglibaoproxy内部类Dynamicadvisedinterceptor调用的,而Dynamicadvisedinterceptor继承了Methodinterceptor,用于调用拦截方法,并从中获得调用链。
如果在同一类中调用方法,则不会被方法拦截器拦截,因此事务不起作用。必须将该方法放入另一类,并通过spring注入。
原因:
Transactional是Spring提供的事务管理注释。
重点在于,Spring采用动态代理(AOP)实现对bean的管理和切片,为我们的每个class生成代理对象。只有在代理对象之间调用时,才能触发切面逻辑。
在同一个class中,方法B调用方法A,调用原对象的方法,而不是代理对象。因此,Spring不能切断此调用,也不能通过注释来保证事务性。
也就是说,如果在同一类中调用方法,则不会被方法拦截器拦截,因此事务不会工作。
解决方法1:
将事务方法放入另一类(或单独打开一层,命名为“事务层”)进行调用,即符合在对象之间调用的条件。
解决方法2:
获取本对象的代理对象,然后调用。具体操作如下:
1) Spring-content.在xml上下文中,增加配置:
2) 在xxxserviceimpl中使用(xxxService)(AopContext.currentProxy(),获得xxxservice的代理类,然后调用事务方法,强行通过代理类,激活事务截面。
解决方法3:
大多数情况下,该方法调用并希望激活事务,因为同一方法包括DAO操作和I/O等耗时操作,不希望耗时的I/O导致事务耗时过长(例如,新产品需要同时写入库存)。此时,I/O可以进行异步操作(如添加线程池),即使添加了事务,也不会导致事务过长,问题可以很容易地解决。
解决方法4:
用@Autowired 注入自己 然后用注入的bean调用自己的方法