Self invocation存在的问题
假设我们有如下的TestService
:
@Service
public class TestServiceImpl implements TestService {
@Override
public void saveAB() {
this.saveA();
this.saveB();
}
@Transactional
@Override
public void saveA() {
System.out.println("saveA");
}
@Transactional
@Override
public void saveB() {
System.out.println("saveB");
}
}
还有如下的TestServiceTest
:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestServiceTest {
@Autowired
private TestService testService;
@Test
public void test() {
testService.saveAB();
testService.saveA();
testService.saveB();
}
}
在TestServiceTest.test()
中,虽然testService.saveA()
和testService.saveB()
会在事务中执行,但是testService.saveAB()
中的this.saveA()
和this.saveB()
都不会在事务中执行,这就是self invocation存在的问题。
为什么self invocation会存在问题?
首先我们必须要清楚Spring AOP是基于代理的,但是在介绍代理之前,先来看看没有代理时是怎么样的。
public class SimplePojo implements Pojo {
@Override
public void foo() {
System.out.println("foo");
this.bar();
}
@Override
public void bar() {
System.out.println("bar");
}
}
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
pojo.foo();
}
}
执行Main.main(String[] args)
,会输出如下内容:
foo
bar
当我们调用pojo.foo()
时,我们是直接调用pojo
的foo()
,如下图所示:
接下来我们就看看调用代理的方法是怎样的。
我们修改Main
为如下所示:
public class Main {
public static void main(String[] args) {
SimplePojo target = new SimplePojo();
ProxyFactory factory = new ProxyFactory(target);
factory.addInterface(Pojo.class);
factory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before invoking method");
}
});
Pojo pojo = (Pojo) factory.getProxy();
pojo.foo();
}
}
执行Main.main(String[] args)
,会输出如下内容(注意前面的foo
前面的before invoking method
):
before invoking method
foo
bar
这里的pojo是一个代理,当我们调用pojo.foo()
时,其执行情况如下图所示:
当调用pojo.foo()
时,其实是调用代理的foo()
,这个时候会打印出before invoking method
,之后调用会来到target object,调用target
的foo()
,这时会打印出foo
,在target
的foo()
中会调用this.bar()
,而这个this
不是代理,而是target
,这就是为什么没在打印出bar
之前打印before invoking method
的原因。
怎么解决?
Spring proxy self-invocation • my2cents列出了很多种方法来解决这个问题,下面我只介绍我个人认为最简单的方法,我们将TestServiceImpl修改为如下所示(注意看注释)。
@Service
public class TestServiceImpl implements TestService {
private final TestService self;
// 注入自身,一定要加上@Lazy
public TestServiceImpl(@Lazy TestService self) {
this.self = self;
}
@Override
public void saveAB() {
// 将this替换为self
self.saveA();
self.saveB();
}
@Transactional
@Override
public void saveA() {
System.out.println("saveA");
}
@Transactional
@Override
public void saveB() {
System.out.println("saveB");
}
}
这个时候再执行TestServiceTest.test()
,会发现testService.saveAB()
中的saveA()
和saveB()
都会在事务中执行。