以下內容首發於我的個人博客網站:
http://riun.xyz
參考鏈接: https://www.cnblogs.com/moxiaotao/p/9776964.html
同一個類中,在一個沒有註解的方法A中調用另外一個有註解(比如@Async, @Transational)的方法B時,是不會觸發B方法的註解的。
比如下面的例子:
有註解標註的方法addUser();沒有註解標註的方法selUser()。在selUser()中調用addUser()則不會觸發@Transactional註解。
@Service
public class UserService {
@Resource
private TestMapper testMapper;
@Transactional
public void addUser() {
Test test = new Test();
test.setName("888");
test.setAge(888);
testMapper.insertSelective(test);
}
public Test selUser() {
Test test = testMapper.selectByPrimaryKey(1);
addUser();//測試@Transactional是否起作用
System.out.println(test);
return test;
}
}
我們寫一個簡單的SpringBoot項目將這個service嵌進去。將日誌級別設置爲debug,這樣能詳細的看到是否執行了事務。
調用addUser()方法時,創建了一個事務去執行:
2020-05-07 14:58:01.563 DEBUG 101548 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [com.example.demo.service.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
而調用selUser()方法時,並沒有創建事務執行:
這驗證了上面的結論。那這又是爲什麼呢?
Spring在掃描bean時會掃描方法上的註解,當添加了@Async, @Transational等這些註解時,spring會爲這個bean自動生成一個子類【代理類,proxy,關於代理的文章點這裏】,此子類繼承原來的類,就是原來類的代理類。【比如例子中向addUser()添加註解@Transational時,spring掃描到時會生成一個繼承於UserService的代理類UserServiceproxy】,代理類中爲有註解的方法插入一個 startTransaction ()方法。
僞代碼:
@Service
public class UserServiceproxy$A {
UserService userService = new UserService();
public void addUser() {
startTransaction();
userService.addUser();
}
/*
只有直接調用UserServiceproxy$A.addUser()時,纔會調用到startTransaction();
*/
public Test selUser() {
userService.selUser();
/*
內部調用 userService.addUser()
而不是 UserServiceproxy$A.addUser()
*/
}
}
此時,當有註解的方法addUser()被調用時,實際上是調用代理類UserServiceproxy$A的addUser(),所以會執行到startTransaction(); 就能夠開啓事務。而調用沒有註解的方法selUser()時,內部就是直接調用的自身的selUser(),自身的selUser()內部調用的是自身的addUser(),所以並不會開啓事務。
那麼如何解決這個問題呢?有兩種方法:
-
1、把這兩個方法分開寫到不同的類中(只需寫在不同類中,selUser()方法不用添加註解);
-
2、把註解加到類名上面(這樣該類下的所有方法都擁有此註解的效果);