人最宝贵的是生命,生命属于人只有一次。人的一生应当这样度过:当他回首往事时,不会因虚度年华而悔恨,也不会因碌碌无为而羞耻。这样,临终前他就可以自豪地说:“我已经把自己整个生命和全部精力都献给了世界上最壮丽的事业——为人类的解放而奋斗。”——《钢铁是怎样炼成的》
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对象。通过类去调用。激活事务切面。
获取上下文工具类:
@Componentpublic class BeanUtil implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext context) throws BeansException {applicationContext = context;}/*** 获取applicationContext*/public static ApplicationContext getApplicationContext() {return applicationContext;}/*** 通过class获取Bean** @param clazz* @param <T>* @return*/public static <T> T getBean(Class<T> clazz) {return getApplicationContext().getBean(clazz);}}
调用方法:
TestTransactional testTransactional = BeanUtil.getBean(TestTransactional.class);testTransactional.A();
由于事务的实现原理是切面,因此我们在遇到切面相关的失效问题,比如自定义注解等因为方法未通过代理类调用的原因,因此都可以通过本文的两种方法解决。