1. 程式人生 > >Springboot中的快取Cache和CacheManager原理介紹

Springboot中的快取Cache和CacheManager原理介紹

背景理解

什麼是快取,為什麼要用快取

程式執行中,在記憶體保持一定時間不變的資料就是快取。簡單到寫一個Map,裡面放著一些key,value資料,就已經是個快取了

所以快取並不是什麼高大上的技術,只是個概念,把要多次使用的東西存在一個變數裡,時不時取出來使用,就達到了快取的目的,快取就是存放資料的容器

那為什麼要用快取呢,是因為要多次使用。一個程式總有一些資料時可預見被多次使用(預見的準不準就是常說的命中率)

比如一個複雜的計算結果,一次資料庫訪問取得的資料等耗時耗資源的資料就能放入快取,目的就是為了節省開銷,我們要用有限的資源(CPU,記憶體,頻寬等等)儘量做最多的事情。

為什麼要用SpringCache(快取的演變過程)

快取的思考

如果我們要設計一個快取,最基本的功能是存和取:

1.能在快取裡存放資料

2.能在快取裡取出資料

可是這不夠呀,比如以下的思考

1.取資料時判斷,資料是否存在,如果不存在是不是要資料庫取

2.如果是過期的內容是不是要更新

3.如果我有多個快取,一個是我自己設計的HashMap快取,一個是名聲很大的redis,還有....,那需要個快取管理器呀

為了讓快取更好用,更“智慧”,越來越多的需求就會被提出來,而快取就是這樣一步步演變直到SpringCache橫空出世,功能十分強大(說白了就是我們少寫很多程式碼)

SpringCache的好處

SpringCache包含兩個頂級介面,Cache(快取)和CacheManager(快取管理器),顧名思義,用CacheManager去管理一堆Cache。

最最關鍵的地方:抱緊了Spring的大腿,可以使用註解就能完成資料進入快取!!

給大家舉個例子,就知道多簡單了

首先,Springboot中會自動載入一個CacheManager(它有預設的實現類),所以只要寫好一個自定義的Cache即可(如果想用系統定義好的或者第三方如RedisCache也行,記得向Spring註冊這個bean即可)

@Component
public class MyCache implements Cache {
  /*
       實現介面方法,一些關於資料set和get的方法
       CacheManager是根據Cache的名字進行管理的
       所以假設這個Cache名為MyCache
  */  
}

然後在得出資料的方法上寫上註釋即可

@Cacheable(value = "MyCache",key = "#id")
public String getNavegationURLs(String id) {
        //一個獲取資料的方法
}

這樣就會在呼叫這個方法時,會以id為key值,在名為MyCache的Cache容器中查詢(註解中value就是快取名字,不同名字指定使用不同的快取)

如果沒查到,則執行方法 getNavegationURLs,將返回值存入快取

如果找到了,就直接將從快取取值,直接返回,不用執行方法 getNavegationURLs

還有其他方便的Cache註解自行百度,重要的是我們根本不用寫任何關於呼叫快取的邏輯程式碼,只用關注於快取自身的邏輯

註解如何起作用的,原始碼流程大致瞭解

為什麼要了解原始碼

最直接的原因是因為SpringCache是不支援靈活的快取時間設定的,所以想了解大概的來龍去脈去實現一個支援快取過期時間設定和自動更新的類(之後會寫實現博文)。

高大上的原因是想通過這次探索,去了解下Spring對類的管理機制,去接觸下AOP的實現

SpringCache原始碼簡單分析

大家從上面例子有沒發現問題,Cache和CacheManager是怎樣做關聯的,其實是Spring掃包實現的

凡是繼承了Cache介面的類,都會被自動注入進CacheManager中,最終儲存於CacheManager的實現類中

 

 接著會生成被@Cacheable(或者其他SpringCache註解修飾過)的代理類,並會將管理它的CacheManager賦值進去

 看這段程式碼,就知道如果要設定多個CacheManager,就得在眾多實現類的其中一個加上@Primary,不然會Spring會報錯能選擇的Bean太多而不知道用哪個

 

代理類生成後(包括會根據不同的註解生成資訊類CacheOperationMetadata,到時候就會根據這個類的內容進行快取操作,說白了就是呼叫我們實現Cache裡面的各種方法)

Springboot底層初始化完成後,進入我們寫的程式碼邏輯

如果這時進入了該類的方法,如:

 

 程式碼跟進去,你會神奇的發現進入了代理類的intercept方法,怎麼進去的呢~(具體原理看下面3.0)

 這裡面就會根據註解型別,進行快取的邏輯判斷,然後決定會不會呼叫我們寫的方法~

 代理類原理介紹(AOP切面之類的都是通過代理哦)

Spring代理分為兩種:

1.JDK原生動態代理,要求被代理的類需要實現介面(通過介面來實現的代理

那麼代理類滿足以下條件:

首先實現一個InvocationHandler,方法呼叫會被轉發到該類的invoke()方法。 意思是:對代理物件的所有介面方法呼叫都會轉發到InvocationHandler.invoke()方法,在invoke()方法裡我們可以加入任何邏輯,比如修改方法引數,加入日誌功能、安全檢查功能等;之後我們通過某種方式執行真正的方法體   2.CGLIB動態代理,不要求被代理的類需要實現介面,但是final的方法無法被代理(通過繼承來實現代理) 那麼代理類滿足以下條件: 實現一個MethodInterceptor,方法呼叫會被轉發到該類的intercept()方法

具體內容可以參考這篇精品部落格:https://www.cnblogs.com/CarpenterLee/p/8241042.html

如果你想自己實現代理類(就是不喜歡用工具包),其實也行啊,輸出符合class規範的二進位制位元組碼就行啦~~~(認真學習JVM規範吧)

 

至此,該分享的就分享完啦,有什麼問題歡迎留言一起探討~

 

 

 

 

&n