SpringBootCache源碼解析:默認Cache配置
默認 Cache 配置
當使用@EnableCachina 啟動 Spring Boot 的緩存機制但又未添加其他緩存類庫時,SpringBoot 會默認提供一個基 于 ConcurrentHashMap 實現(xiàn)的緩存組件
--ConcurrentMap-CacheManager。但官方文檔已經(jīng)明確提示,不建議在生產(chǎn)環(huán)境中使用該緩存組件。但它卻是一個很好的學習緩存特性的工具。
這個默認的緩存組件是通過 SimpleCacheConfiguration 來完成自動配置的。下面,我們簡單了解一下它的自動配置以及 ConcurrentMapCacheManager 的實現(xiàn)。
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition. class)
class SimpleCacheConfiguration {
@Bean
ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers) {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager
();
List cacheNames = cacheProperties . getCacheNames();
if (!cacheNames. isEmpty()) {
cacheManager . setCacheNames (cacheNames);
}
return cacheManagerCustomizers . customize(cacheManager);}
} 該 自 動 配 置 文 件 很 簡 單 , 當 容 器 中 不 存 在 CacheManager 的 Bean, 同 時 滿 足CacheCondition 中指定的條件時,則進行自動配置。關(guān)于 CacheCondition 中的業(yè)務(wù)邏輯實現(xiàn)已經(jīng)在上一節(jié)進行了詳細地講解,不再贅述。
在 cacheManager 方法中首先創(chuàng)建了- -個 ConcurrentMapCacheManager 對象,然后通過配置屬性類獲得緩存名稱列表,如果列表內(nèi)容不為空,則賦值給上述對象 cacheManager。
最后調(diào)用 CacheManagerCustomizers 的 customize 方法對 cacheManager 進行定制化處理并返回。
下面我們重點看 cacheManager 方法中 ConcurrentMapCacheManager 類的內(nèi)部實現(xiàn)。通過名字就可以看出它是基于 ConcurrentHashMap 來實現(xiàn)的,它是接口 CacheManager 的實現(xiàn)類,同時也實現(xiàn)了 BeanClassLoaderAware 接口用來獲取 SerializationDelegate 及進行一些初始化操作。

首先看 ConcurrentMapCacheManager 的成員變量部分源代碼。
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLo
aderAware {
// ConcurrentMapCacheManager 緩存的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),用于存儲緩存數(shù)據(jù) private final
ConcurrentMap cacheMap = new ConcurrentHashMap<>(16);
//是否為動態(tài)創(chuàng)建緩存
private boolean dynamic = true;
//是否允許 null 值
private boolean allowNullValues = true;
//是否存儲 value 值
private boolean storeByValue = false;
//用于序列化的委托類,通過實現(xiàn) BeanClassLoaderAware 接口注入,用子值的序列化和反
序列號
@Nullable
private SerializationDelegate serialization;
} 在 ConcurrentMapCacheManager 中定義了 ConcurrentMap
這里采用的 ConcurrentHashMap 是 Java 中的一個線程安全且高效的 HashMap 實現(xiàn)。
dynamic 屬性定義了緩存是動態(tài)創(chuàng)建還是靜態(tài)創(chuàng)建,true 表示動態(tài)創(chuàng)建 ,false 表示靜態(tài)創(chuàng)建,后面在涉及具體的方法功能時會用到; allowNullValues 用來表示是否允許 nul 值;
storeByValue 表示是否需要存儲值, 如果需要存儲值則需配合 serialization 順序性進行序列化和反序列號操作。這里的存儲值指的是復制的 value 值,與存儲引用相對,存儲值(復制的 value 值)時才會進行序列化和反序列化。

下面從 ConcurrentMapCacheManager 的構(gòu)造方法開始來進行相關(guān)方法的講解。
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware.
//實現(xiàn)為空的構(gòu)造方法,用于構(gòu)建.
動態(tài)的 ConcurrentMapCacheManager,
//當緩存實例被請求時進行懶加裁
public ConcurrentMapCacheManager() {
//構(gòu)建一個靜態(tài)的 ConcurrentMapCacheManager, 僅管理捐定緩存名稱的緩存
public ConcurrentMapCacheManager(Stri
, cacheNames) {
setCacheNames (Arrays . aslist(cacheNames));
/設(shè)置緩存名稱或重置緩存模式
public void setCacheNames (@Nullable Collection cacheNames) {
if (cacheNames != null)
for (String name : cacheNames){
this . cacheMap. put(name, createConcurrentMapCache(name));
this.dynamic = false;
} else
this . dynamic = true;
}
}
} ConcurrentMapCacheManager 提供了兩個構(gòu)造方法,第一-個構(gòu)造方法用于構(gòu)建一個動態(tài)的 ConcurrentMapCacheManager, 構(gòu)造方法實現(xiàn)為空。在自動配置中便是采用的該構(gòu)造方法,默認情況下,dynamic 屬 性的為 true,即動態(tài)構(gòu)建,當緩存實例被請求時進行懶加載。
另外一個構(gòu)造方法的參數(shù)為不定參數(shù),構(gòu)造方法內(nèi)的核心操作就是調(diào)用 setCacheNames 方法。在 setCacheNames 方法內(nèi)部, 如果 cacheNames 不為 null,也就是采用“靜態(tài)”模式,會遍歷緩存名稱,并初始化 cacheMap 中的值。這里需要注意的是,一旦進入該業(yè)務(wù)邏輯操作,也就意味著緩存的屬性及名稱將被固定,運行時不會再創(chuàng)建其他緩存區(qū)域。
那么,如果想改變這種“不變"的情況該如何處理?還是調(diào)用該方法,設(shè)置參數(shù) cacheNames為 null,此時執(zhí)行 else 中的邏輯,將 dynamic 設(shè)置 為 true,即靜態(tài)模式重置為動態(tài)模式,從而允許再次創(chuàng)建緩存區(qū)域。setCacheNames 方法為 public,因此不僅構(gòu)造方法可以調(diào)用,其實例化對象也可以直接調(diào)用進行設(shè)置。
在第二個構(gòu)造方法中調(diào)用了當前類的 createConcurrentMapCache 方法,代碼如下。
protected Cache createConcurrentMapCache(String name) {
SerializationDelegate actualSerialization = (isStoreByValue() ? this.seri
alization : null);
return new ConcurrentMapCache(name, new ConcurrentHashMap<> (256),
isAllowNullValues(),actualSerialization);
}createConcurrentMapCache 方法的主要作用就是創(chuàng)建一個 ConcurrentMapCache,它是Cache 接口的簡單實現(xiàn)。該方法內(nèi),首先根據(jù)屬性 storeByValue 的值判斷是否需要Serializa-tionDelegate 來進行序列化操作,如果不需要則將 SerializationDelegate 設(shè)置為null。然后,將緩存名稱、緩存值、是否允許 Nul1 值和序列化委托類當作構(gòu)造參數(shù)創(chuàng)建Concurrent-MapCache 類并返回。
在 ConcurrentMapCacheManager 中有-個私有的 recreateCaches 方法,在滿足條件的情況下會遍歷 cacheMap 并調(diào)用上面 createConcurrentMapCache 方法進行緩存的重置操作。
private void recreateCaches() {
for (Map. Entry entry : this. cacheMap .entrySet()) {
entry . setValue(createConcurrentMapCache(entry . getKey()));
} 當 allowNullValues 或 storeByValue 的值通過 set 方法改變時,均會調(diào)用 recreateCaches方法進行緩存的重置。
最后看 ConcurrentMapCacheManager 對 getCacheNames 和 getCache 方法的實現(xiàn)。
@Override
public Collection getCacheNames() {
return Collections . unmodifiableSet(this . cacheMap. keySet());
@Override
@Nullable
public Cache getCache(String name) {
Cache cache = this. cacheMap . get(name);
if (cache == null && this. dynamic) {
synchronized (this . cacheMap) {cache = this. cacheMap . get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this. cacheMap . put(name, cache);
}
}
}
return cache; } getCacheNames 方法直接獲取 cacheMap 中 name 的 Set,并通過 Collections 類將其設(shè)置為不可變的集合并返回。
getCache 方法首先根據(jù) name 從 cacheMap 中獲取 Cache 值,如果值為 null 并且是動態(tài)模式,則對 cacheMap 加鎖同步, 重新獲取判斷,如果 cache 依舊為 null,則調(diào)用create-ConcurrentMapCache 方 法創(chuàng)建并給 cacheMap 賦值。否則,直接返回 cache 值。
至此,關(guān)于 ConcurrentMapCacheManager 的基本功能也講解完畢。在此提醒一下,這只是一一個簡單的 CacheManager,并沒有緩存配置項,僅可用于測試環(huán)境和簡單的緩存場景。
對于高級的本地緩存需求建議使用 JCacheCacheManager、EhCacheCacheManager、CaffeineCacheManager 等方法。
最后,我們再稍微拓展一下上面提到的 ConcurrentMapCache 類,該類實現(xiàn)了 Cache 接口,提供了緩存值的存儲和獲取等實現(xiàn)。而這些功能的實現(xiàn)就是圍繞上面提到構(gòu)造該類時傳入的參數(shù)展開的。
以 ConcurrentMapCache 的 put 方法及相關(guān)方法為例,我們簡單說一下它的實現(xiàn)過程。
@Override
public void put(0bject key, @Nullable object value) {
this . store. put(key, toStoreValue(value));
@Override
protected object toStoreValue (@Nullable object userValue) {
//父類的 toStoreValue 方法實現(xiàn),用于檢查是否允許 null。如果值為 null 且允許為 null,則返
@NullValue.
INSTANCE
Object storeValue = super . toStoreValue(userValue);
if (this. serialization != null) {
try {
//對質(zhì)進行序列化
return serializeValue(this . serialization, storeValue);
catch (Throwable ex) {throw new IllegalArgumentException("Failed to serialize cache valueuserValue +
"'. Does it implement Serializabl
e?", ex);
} else {
return storeValue;
//通過傳入的 Serial izationDelegate 序列化緩存值為 byte 數(shù)組
private object serializeValue(SerializationDelegate serialization, object s
toreValue) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
serialization. serialize(storeValue, out);
return out. toByteArray();
} finally {
out. close();
}上面為 put 方法涉及的操作,基本步驟是如下。
.判斷待設(shè)置的 userValue 的值是否為 mull,如果為 mull 且允許存儲 mull 值,返回NullValue.INSTANCE。否則,直接返回 userValue 值。以上值均賦值給 storeValue。
如果序列化委托類(serialization )不為 null, 則通過 SerializationDelegate 對 storeValue 值進行序列化操作。如果為 mull,則直接返回 store Value。
.無論經(jīng)過以上步驟獲得的是原始傳入值、NullValue.INSTANCE 或是經(jīng)過序列化的字節(jié)數(shù)組 , 都 通 過 store 的 put 方 法 將 其 存 儲 。store 的數(shù) 據(jù) 結(jié) 構(gòu) 為Concurrent-Map
其中第一個 Object 為緩存的 key,第二個 Object 為緩存的具體數(shù)據(jù)。
至此,Spring Boot 默認,的。Cache 配置就講解完畢了,關(guān)于滾動鼠標軸或單擊,開始截長圖 ConcurrentMapCache 類的其他方法實現(xiàn),讀者朋友可自行閱讀相關(guān)源碼,不過基本上都是圍繞上面提到的一-些屬性和數(shù)據(jù)結(jié)構(gòu)展開的。

小結(jié)
本章重點介紹了 Spring Boot 中緩存的自動配置以及基于 ConcurrentHashMap 實現(xiàn)的最簡單 的 緩 存 功 能 。涉 及 的 緩 存 實 現(xiàn) 都 只 是 基 于 Java 提 供 的 數(shù) 據(jù) 結(jié) 構(gòu) (Collection 、ConcurrentHashMap) 存儲來實現(xiàn)的。而在實戰(zhàn)過程中,根據(jù)不同的場景會使用不同的三方緩存組件,比如 JCache、EhCache、Caffeine、 Redis 等。但基本的實現(xiàn)原理一致,讀者朋友可參照本章內(nèi)容進行具體的分析學習。
本文給大家講解的內(nèi)容是SpringBootCache源碼解析:默認Cache配置
下篇文章給大家講解的是Spring Boot 日志源碼解析;
覺得文章不錯的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;
感謝大家的支持!
本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學習更多的話可以到微信公眾號里找我,我等你哦。
