1. 程式人生 > >在同一個類中呼叫其他有註解(如@Async,@Transactianal)的方法,註解失效問題

在同一個類中呼叫其他有註解(如@Async,@Transactianal)的方法,註解失效問題

目錄

在同一個類中,一個方法呼叫同類中的其他有註解的方法註解是不會生效的

Spring在初始化的時候會掃描方法上面的註解,如果好辦@Async註解spring會生成並注入一個繼承我們bean的代理類(子類),在執行此方法的時候,會到代理類中判斷此方法需要非同步執行,就不會呼叫父類(我們原來寫的bean)的對應方法。spring自己維護了一個佇列,他會把需要執行的方法,放入佇列中,等待執行緒池去讀取這個佇列,完成方法的執行,從而完成了非同步的功能。我們可以關注到在配置task的時候,有引數讓我們配置執行緒池的數量。因為這種實現方法,所以在同一個類中呼叫添加註解的方法是失效的!因為當你在同一個類中,方法呼叫是在實體內執行的,spring無法截獲到這個方法呼叫。

For example:

比如,下面程式碼例子中,有兩方法,一個有@Transational註解,一個沒有。如果呼叫了有註解的addPerson()方法,會啟動一個Transaction;如果呼叫updatePersonByPhoneNo(),因為它內部呼叫了有註解的addPerson(),如果你以為系統也會為它啟動一個Transaction,那就錯了,實際上是沒有的。

@Service  
public class PersonServiceImpl implements PersonService {  

 @Autowired  
 PersonDao personDao;  

 @Override
@Transactional public boolean addPerson(Person person) { boolean result = personDao.insertPerson(person)>0 ? true : false; return result; } @Override //@Transactional public boolean updatePersonByPhoneNo(Person person) { boolean result = personDao.updatePersonByPhoneNo(person)>0
? true : false; addPerson(person); //測試同一個類中@Transactional是否起作用 return result; } }

如何檢視是否啟動了Transaction?
設定log leve為debug,可以檢視是否有下面這個log,判斷是否啟動了Transaction:
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name…

同樣的,@Async等其他註解也有這樣的問題。 (@Async的用法

原因:

spring 在掃描bean的時候會掃描方法上是否包含@Transactional註解,如果包含,spring會為這個bean動態地生成一個子類(即代理類,proxy),代理類是繼承原來那個bean的。此時,當這個有註解的方法被呼叫的時候,實際上是由代理類來呼叫的,代理類在呼叫之前就會啟動transaction。然而,如果這個有註解的方法是被同一個類中的其他方法呼叫的,那麼該方法的呼叫並沒有通過代理類,而是直接通過原來的那個bean,所以就不會啟動transaction,我們看到的現象就是@Transactional註解無效。

為什麼一個方法a()呼叫同一個類中另外一個方法b()的時候,b()不是通過代理類來呼叫的呢?可以看下面的例子(為了簡化,用偽程式碼表示):


@Service  
class A{  
    @Transactinal  
    method b(){...}  

    method a(){    //標記1  
        b();  
    }  
}  

//Spring掃描註解後,建立了另外一個代理類,併為有註解的方法插入一個startTransaction()方法:  
class proxy$A{  
    A objectA = new A();  
    method b(){    //標記2  
        startTransaction();  
        objectA.b();  
    }  

    method a(){    //標記3  
        objectA.a();    //由於a()沒有註解,所以不會啟動transaction,而是直接呼叫A的例項的a()方法  
    }  
}  

當我們呼叫A的bean的a()方法的時候,也是被proxyAproxyA.a()(標記3),然而,由以上程式碼可知,這時候它呼叫的是objectA.a(),也就是由原來的bean來呼叫a()方法了,所以程式碼跑到了“標記1”。由此可見,“標記2”並沒有被執行到,所以startTransaction()方法也沒有執行。

解決辦法

  1. 把這兩個方法分開到不同的類中;
  2. 吧註解加到類名上面;

轉自作者 Clement-Xu ,感謝作者的無私分享。

那再深入一步,spring為我們提供了AOP,面向切面的功能。他的原理和非同步註解的原理是類似的,spring在啟動容器的時候,會掃描切面所定義的 類。在這些類被注入的時候,所注入的也是代理類,當你呼叫這些方法的時候,本質上是呼叫的代理類。通過代理類再去執行父類相對應的方法,那spring只 需要在呼叫之前和之後執行某段程式碼就完成了AOP的實現了!

那最後我們還有一個問題,spring是如何動態的生成某一個類的子類的?代理類?