Self invocation存在的問題
假設(shè)我們有如下的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()
會(huì)在事務(wù)中執(zhí)行秒紧,但是testService.saveAB()
中的this.saveA()
和this.saveB()
都不會(huì)在事務(wù)中執(zhí)行,這就是self invocation存在的問題窒朋。
為什么self invocation會(huì)存在問題?
首先我們必須要清楚Spring AOP是基于代理的,但是在介紹代理之前,先來看看沒有代理時(shí)是怎么樣的弊添。
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();
}
}
執(zhí)行Main.main(String[] args)
,會(huì)輸出如下內(nèi)容:
foo
bar
當(dāng)我們調(diào)用pojo.foo()
時(shí)捌木,我們是直接調(diào)用pojo
的foo()
油坝,如下圖所示:
接下來我們就看看調(diào)用代理的方法是怎樣的。
我們修改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();
}
}
執(zhí)行Main.main(String[] args)
刨裆,會(huì)輸出如下內(nèi)容(注意前面的foo
前面的before invoking method
):
before invoking method
foo
bar
這里的pojo是一個(gè)代理澈圈,當(dāng)我們調(diào)用pojo.foo()
時(shí),其執(zhí)行情況如下圖所示:
當(dāng)調(diào)用pojo.foo()
時(shí)帆啃,其實(shí)是調(diào)用代理的foo()
瞬女,這個(gè)時(shí)候會(huì)打印出before invoking method
,之后調(diào)用會(huì)來到target object努潘,調(diào)用target
的foo()
诽偷,這時(shí)會(huì)打印出foo
,在target
的foo()
中會(huì)調(diào)用this.bar()
疯坤,而這個(gè)this
不是代理报慕,而是target
,這就是為什么沒在打印出bar
之前打印before invoking method
的原因压怠。
怎么解決眠冈?
Spring proxy self-invocation ? my2cents列出了很多種方法來解決這個(gè)問題,下面我只介紹我個(gè)人認(rèn)為最簡單的方法菌瘫,我們將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");
}
}
這個(gè)時(shí)候再執(zhí)行TestServiceTest.test()
突梦,會(huì)發(fā)現(xiàn)testService.saveAB()
中的saveA()
和saveB()
都會(huì)在事務(wù)中執(zhí)行。