场景
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通知,内部调用都会受到同样限制