Java中嵌套調用方法時註解失效的原因

以下內容首發於我的個人博客網站:
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、把註解加到類名上面(這樣該類下的所有方法都擁有此註解的效果);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章