同一个类中无事务方法调用同一个类的有事务方法问题原因及其解决方法

无事务a()方法中调用同一个类的有事务b()方法问题原因及其解决方法

在Spring管理的项目中,方法B使用了Transactional注解,试图实现事务性。但当同一个class中的方法A调用方法B时,会发现方法B中的异常不再导致回滚,也即事务失效了。

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

public class UserService extends BaseServerTest {

    private static final Logger logger = Logger.getLogger(PropagationTest.class);

    @Autowired
    UserMapper userMapper;

    @Test
    public void a() {
        try {
            b();
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false, rollbackFor = RuntimeException.class)
    public void b() {
        try {
            userMapper.saveNews(new JSONArray());
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }
}

原因:

声明式事务基于Spring AOP实现,将具体业务逻辑与事务处理解耦,在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题

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

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

Transactional是Spring提供的事务管理注解。


重点在于,Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。

而在同一个class中,方法A调用方法B,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用,也就无法通过注解保证事务性了。

也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用。


解决方法

解决方法1:

将事务方法放到另一个类中(或者单独开启一层,取名“事务层”)进行调用,即符合了在对象之间调用的条件。


解决方法2:

获取本对象的代理对象,再进行调用。具体操作如:

  1. Spring-content.xml上下文中,增加配置:
<aop:aspectj-autoproxy expose-proxy="true"/>
  1. 在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),获取到xxxService的代理类,再调用事务方法,强行经过代理类,激活事务切面。

解决方法3:

很多时候,方法内调用又希望激活事务,是由于同一个方法既有DAO操作又有I/O等耗时操作,不想让耗时的I/O造成事务的太长耗时(比如新增商品同时需要写入库存)。此时,可以将I/O做成异步操作(如加入线程池),而加入线程池的操作即便加入事务也不会导致事务太长,问题可以迎刃而解。


解决方法4:

用@Autowired 注入自己 然后在用注入的bean调用自己的方法也可以

发布了8 篇原创文章 · 获赞 27 · 访问量 458

猜你喜欢

转载自blog.csdn.net/liuguang212/article/details/104695253