一、原因解析
在项目加载的时候,spring会去扫描所有带有@transaction注解 的方法或者类,然后为他们生成一个代理类,然后代理类里面会持有原始类的引用,示例如下:
代理类:
AProxy{
//A的真实对象引用
private Object a;
public AProxy(Object a) {
this.a= a;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在转调具体目标对象之前,可以执行一些功能处理
System.out.println("doSomething before");
// 转调具体目标对象的方法,也就是都Something这个方法
Object o = method.invoke(a, args);
// 在转调具体目标对象之后,可以执行一些功能处理
System.out.println("doSomething after");
return o;
}
}
原始类:
A{
public void doSomething() {
System.out.println("call doSomething()");
doEverything();
}
@transaction
public void doEverything() {
System.out.println("call doEverything(......)");
}
}
也就是说,加了@transaction注解就会有事务属性是因为在代理类里面,spring在调用目标方法之前和之后做了事务的封装,我们在调用doSomething方法时是代理类在调用,而doSomething方法里面调用doEverything方法,实质执行的代码是this.doEverything(),此处this对象是实际的A对象而不是本该生成的代理对象,因此直接调用了doEverything()方法。所以不管doEverything()有没有打上事务注解,都不会生效
二、解决办法:
1、放到不同的类中进行调用
2、将之前使用普通调用的方法,换成使用代理调用
((A)AopContext.currentProxy()).doEverything();
获取到A的代理类,再调用事务方法,强行经过代理类,激活事务切面。