在使用Spring時,很多初學者不了解Spring對象注入的機制和面向切面編程的原理,很容易犯一些錯誤。下面就是初學者最容易犯的錯誤。舉例如下:
@Component public class TestClass {
|
這里TestClass類里同時啟用了Scheduled注解和@Cacheable注解。如果在外面其他類中調用getData(...)時,緩存機制會生效,但是在task函數內部調用getData(...)時,緩存機制不會生效。網上有人把這歸納為:不能在同一個類中互相調用注解過的方法,否則注解失效。
為什么會有這樣的差別呢?背后的原因就是Spring的對象注入機制。
當外部通過@Autowired注解得到一個TestClass對象時,其實得到的是一個Spring包裝過的代理對象。如下圖所示。

當調用Obj.getData時,實際調用的是Spring的Proxy對象中的getData方法,該方法內置了Cache機制,在Cache檢查后就會調用實際的TestClass對象中的getData方法。
同理通過@Scheduled注解表示這是一個任務調度時,Spring Proxy對象中會初始化對應的調度線程池等工作,當觸發調度條件時,再調用實際的TestClass對象。
但是當在實際的TestClass對象中再調用getData時,不會觸發Cache機制,因為此時不是調用的SpringCacheProxy對象,而是一個實際的TestClass對象,所以不會觸發Cache機制。
如果大家深入去讀Spring完成注入和AOP編程實現的原理,可以發現動態代理是很重要的一個技術。目前Spring的動態代理主要是通過CGLib來實現的。后面我會再寫CGLib的實現思路。
那么我們該如何避免出現上述問題呢?
首先我們應該牢記一個原則:同一個類中的注解方法互相調用時,注解機制可能是無效的。
對於上述示例,我們可以把其拆分為兩個類來實現,一個類完成任務調度、一個類完成Cache機制。這樣在任務調度中調用的是Spring實現了Cache機制的代理類,可以確保其Cache機制生效。