@Transactional注解内部调用失效

场景

insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。
上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。

@Controller
class XService {
    
    
    @Autowired
    private YService yService;
    public void doOutside(){
    
    
        this.doInside(); //或者直接doInside();效果是一样的
    }
    @Transactional
    public  void doInside(){
    
    
        //do sql statement
    }
}
@Controller
class Test {
    
    
    @Autowired
    private XService xService;
    public void test(){
    
    
        xService.doOutside();
    }
}

回忆下Java基础,this表示的是类的当前实例,那么关键就是确定类的实例是未被增强的XService(下面称其为XService),还是被CGLIB增强过的XService(下面称其为XServiceCglib)。
在这里插入图片描述
在Test中,XService类的实例变量是一个由Spring框架管理的Bean,当执行test()时,根据@Autowired注解进行相应的注入,因此XService的实例实际为XServiceCglib而不XService。

当执行XServiceCglib.doOutside()时,由于子类没有覆写父类同名方法,因此实际上执行了父类XService的doOutside()方法,所以在执行其this.doInside()时实际上调用的是父类未增强过的doInside(),因此事务管理器失效了。

这个问题在Spring AOP中广泛存在,即自调用,本质上是动态代理无法解决的盲区,只有AspectJ这类静态代理才能解决。

解决办法

方法一:1. 最好在被代理类的外部调用其方法

方法二:2.自注入(Self Injection, from Spring 4.3)

@Controller
class XService {
    
    
    @Autowired
    private YService yService;
    @Autowired
    private XService xService;
    public void doOutside(){
    
    
        xService.doInside();//从this换成了xService
    }
    @Transactional
    private void doInside(){
    
    
        //do sql statement
    }
}
@Controller
class Test {
    
    
    @Autowired
    private XService xService;
    public void test(){
    
    
        xService.doOutside();
    }
}

由于xService变量是被Spring注入的,因此实际上指向XService$$Cglib对象,xService.doInside()因此也能正确的指向增强后的方法。

不仅仅是事务通知,所有你自己利用Spring实现的AOP通知,内部调用都会受到同样限制

猜你喜欢

转载自blog.csdn.net/LOVE_sel/article/details/106205081