Spring事务注解@Transactional失效和切面失效问题

人最宝贵的是生命,生命属于人只有一次。人的一生应当这样度过:当他回首往事时,不会因虚度年华而悔恨,也不会因碌碌无为而羞耻。这样,临终前他就可以自豪地说:“我已经把自己整个生命和全部精力都献给了世界上最壮丽的事业——为人类的解放而奋斗。”——《钢铁是怎样炼成的》

1、引言

最近在开发采用Spring框架的项目中,在A方法上使用了@Transactional注解,但当在同一个类的B方法中调用A方法,发现A发生异常不回滚,A方法的事务注解失效了。

public class TestTransactional {
    @Transactional(propagation = Propagation.REQUIRED)
    public void A() {
        User user = new User("chunsoft");
        userMapper.insertSelective(user);
        if (true) {
            throw new RuntimeException("抛异常");
        }
    }
    
    public void B() {
        this.A();
    }
}

为什么会出现这样的问题呢,我们需要研究下,@Transactional注解的本质:Spring之所以可以对开启@Transactional的方法进行事务管理,是因为Spring为当前类生成了一个代理类,然后在执行相关方法时,会判断这个方法有没有@Transactional注解,如果有的话,则会开启一个事务。 但是,上面这种调用方式时,在调用B()时,使用的并不是代理对象,从而导致this.A()时也不是代理对象,从而导致@Transactional失败。

2、解决方案

(1)首先获取到代理类,再调用事务方法,强行经过代理类,激活事务切面。如:((TestTransactional)AopContext.currentProxy()).A();

(2)通过ApplicationContext上下文获取类的Bean对象。通过类去调用。激活事务切面。

获取上下文工具类:

@Component
public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }

    /**
     * 获取applicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过class获取Bean
     *
     * @param clazz
     * @param 
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }
}

调用方法:

TestTransactional testTransactional = BeanUtil.getBean(TestTransactional.class);
testTransactional.A();

由于事务的实现原理是切面,因此我们在遇到切面相关的失效问题,比如自定义注解等因为方法未通过代理类调用的原因,因此都可以通过本文的两种方法解决。

你可能感兴趣的:(Spring)