SpringBootCache源碼解析:Cache自動(dòng)配置
SpringBootCache源碼解析
Spring Boot 支持了多種緩存的自動(dòng)配置,其中包括 Generic、JCache、EhCache 2.x、Hazelcast、 Infinispan、 Couchbase、 Redis 、Caffeine 和 Simple。早期版本還支持Guava 的緩存,但目前已經(jīng)廢棄。本章將重點(diǎn)講解緩存的自動(dòng)配置 CacheAutoConfiguration和默認(rèn)的 SimpleCacheConfiguration 自動(dòng)配置及相關(guān)內(nèi)容。

Cache 簡介
隨著項(xiàng)目的發(fā)展,往往會(huì)出現(xiàn)- -些瓶頸, 比如與數(shù)據(jù)庫的交互、與遠(yuǎn)程服務(wù)器的交互等。
此時(shí),緩存便派上了用場。而在 Spring 3.1 中引入了基于注解的 Cache 的支持在spring-context 包 中 定 義 了
org.springframework.cache. CacheManager 和org.springframework.cache.Cache 接口,用來統(tǒng)一-不同的緩存的技術(shù)。
CacheManager 是 Spring 提供的各種緩存技術(shù)管理的抽象接口,而 Cache 接口包含緩存的增加、刪除、讀取等常用操作。針對(duì) CacheManager, Spring 又提供了多種實(shí)現(xiàn),比如基于Collection 來 實(shí) 現(xiàn) 的 SimpleCacheManager 、 基 于 ConcurrentHashMap 實(shí) 現(xiàn) 的
Concurrent-MapCacheManager、基于 EhCache 實(shí)現(xiàn)的 EhCacheCacheManager 和基于JCache 標(biāo)準(zhǔn)實(shí)現(xiàn)的 JCacheCacheManager 等。
Spring Cache 的實(shí)現(xiàn)與 Spring 事務(wù)管理類似,都是基于 AOP 的方式。其核心思想是:第一次調(diào)用緩存方法時(shí),會(huì)把該方法參數(shù)和返回結(jié)果作為鍵值存放在緩存中,當(dāng)同樣的參數(shù)再次請(qǐng)求方法時(shí)不再執(zhí)行該方法內(nèi)部業(yè)務(wù)邏輯,而是直接從緩存中獲取結(jié)果并返回。
Spring Cache 提供了@CacheConfig、@Cacheable 、@CachePut 、@CacheEvict 等注解來完成緩存的透明化操作,相關(guān)功能如下。.@CacheConfig:用于類上,緩存一些公共設(shè)置。
.@Cacheable:用于方法上,根據(jù)方法的請(qǐng)求參數(shù)對(duì)結(jié)果進(jìn)行緩存,下次讀取時(shí)直接讀取緩存內(nèi)容。
.@CachePut: 用于方法上,能夠根據(jù)方法的請(qǐng)求參數(shù)對(duì)其結(jié)果進(jìn)行緩存,和@Cacheable不同的是,它每次都會(huì)觸發(fā)真實(shí)方法的調(diào)用。
.@CacheEvict: 用于方法上,清除該方法的緩存,用在類上清除整個(gè)類的方法的緩存。
在了解了 Spring Cache 的基本作用的和定義之后,下面來看在 SpringBoot 中是如何對(duì)Cache 進(jìn)行自動(dòng)配置的。

Cache 自動(dòng)配置
在 Spring Boot 中,關(guān)于 Cache 的默認(rèn)自動(dòng)配置類只有 CacheAutoConfiguration,主要用于緩存抽象的自動(dòng)配置,當(dāng)通過@EnableCaching 啟用緩存機(jī)制時(shí),根據(jù)情況可創(chuàng)建CacheManager。對(duì)于緩存存儲(chǔ)可以通過配置自動(dòng)檢測或明確指定。
CacheAutoConfiguration 同樣在 ME TA-INF/spring.factories 文件中配置注冊(cè)。
# . Auto Configure
org. springframework. boot. autoconfigure . EnableAutoConfiguration=\
org. springframework . boot . autoconfigure . cache . CacheAutoConfiguration, \
下面先來看 CacheAutoConfiguration 類的注解部分代碼實(shí)現(xiàn)。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass (CacheManager. class)
@ConditionalOnBeanCacheAspectSupport . class)
@ConditionalOnMissingBean(value = CacheManager .class, name = "cacheResolve
r")
@EnableConfigurationProperties (CacheProperties. class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration. class, HazelcastAutoConfig
uration. class,
HibernateJpaAutoConfiguration. class, RedisAutoConfigur
ation.class })
@Import(CacheConfigurat ionImportSelector.class)
public class CacheAutoConfiguration {
}@ConditionalOnClass 指 定 需 要 在 classpath 下 存 在 CacheManager 類 。關(guān) 于CacheManager 類是一個(gè)緩存管理器的接口,管理各種緩存(Cache) 組件。針對(duì)不同的緩存技術(shù),會(huì)有不同的實(shí)現(xiàn)類。比如,在 Spring 中提供了 SimpleCacheManager (測試)、
Concurrent-MapCacheManager ( 默 認(rèn) ) 、 NoOpCacheManager ( 測 試 ) 、EhCacheCacheManager ( 基于 EhCache)、RedisCacheManager (基于 Redis) 等實(shí)現(xiàn)類。CacheManager 接口提供了兩個(gè)方法:根據(jù)名稱獲取緩存和獲取緩存名稱集合,相關(guān)代碼如下。
public interface CacheManager {
//根據(jù)名稱獲取緩存
@Nullable
Cache getCache(String name);
//獲取緩存名稱集合
Collection getCacheNames() ;
}在 CacheManager 接 口 中 只 定 義 了 上 面 兩 個(gè) 方 法 , 但 在 其 抽 象 實(shí) 現(xiàn) 類AbstractCache-Manager 中便擴(kuò)展了新增 Cache、更新 Cache 等方法。
@ConditionalOnBean 指定需要存在 CacheAspectSupport 的 Bean 時(shí)才生效,換句話說,就 是 需要在使用 了 @EnableCaching 時(shí) 才 有 效 。這 是 因 為 該 注 解 隱 式 的 導(dǎo) 致 了CacheInter-ceptor 對(duì)應(yīng)的 Bean 的初始化,而 CacheInterceptor 為 CacheAspectSupport的子類。
@ConditionalOnMissingBean 指定名稱為 cacheResolver 的 CacheManager 對(duì)象不存在時(shí)生效。
@
EnableConfigurationProperties 加 載 緩 存 的 CacheProperties 配 置 項(xiàng) , 配 置 前 綴 為spring.cache.
@AutoConfigureAfter 指定該自動(dòng)配置必須在緩存數(shù)據(jù)基礎(chǔ)組件自動(dòng)配置之后進(jìn)行,這里包括 Couchbase、 Hazelcast、 HibernateJpa 和 Redis 的自動(dòng)配置
想要實(shí)現(xiàn)緩存,需要先集成對(duì)應(yīng)的緩存框架或組件。這里以 Redis 為例,它的自動(dòng)配置類RedisAutoConfiguration 中 便 完 成 了 Redis 相 關(guān) 的 Redis Template 和 StringRedisTemplate 的實(shí)例化。而 RedisAutoConfiguration 中導(dǎo)入類
JedisConnectionConfiguration又完成了 Redis 使用 Jedis 連接的配置
@Ilmport 導(dǎo)入
CacheConfigurationlmportSelector,其實(shí)是導(dǎo)入符合條件的 Spring Cache 使用的各類基礎(chǔ)緩存框架(或組件)的配置。該類的實(shí)現(xiàn)就位于 CacheAutoConfiguration 中,代碼如下。
static class CacheConfigurat ionImportSelector implements ImportSelector {
public String[] selectImports(Annotat ionMetadata importingClassMetadata)
CacheType[] types = CacheType.values();
for(inti=0:i1t+)5
imports[i] = CacheConfigurations . getConfigurat ionClass(types[i]);
return imports; }
} 導(dǎo)入類的獲取是通過實(shí)現(xiàn) ImportSelector 接口來完成的,具體獲取步驟位于 selectlmports方法中。該方法中,首先獲取枚舉類 CacheType 中定義的緩存類型數(shù)據(jù),CacheType 中定義支 持的緩存類型如下。
//支持的緩存類型(按照優(yōu)先級(jí)定義)
public enum CacheType {
//使用上下文中的 Cache Bean 進(jìn)行通用緩存
GENERIC,
// JCache(JSR- 107) 支持的緩存
JCACHE,
// EhCache 支持的緩存
EHCACHE,
// Hazelcast 支持的緩存
HAZELCAST,
// Infinispan 支持的緩存
INFINISPAN,
// Couchbase. 支持的緩存
COUCHBASE,
// Redis.支持的緩存
REDIS,
// Caffeine 支持的緩存
CAFFEINE,
//內(nèi)存基本的簡單緩存
SIMPLE,
// 不支持緩存
NONE
}枚舉類 CacheType 中定義了以上支持的緩存類型,而且上面的緩存類型默認(rèn)是按照優(yōu)先級(jí)從前到后的順序排列的。
selectlmports 方法中,當(dāng)獲取 Cache Type 中定義的緩存類型數(shù)組之后,遍歷該數(shù)組并通過CacheConfigurations 的 getConfigurationClass 方法獲得每種類型緩存對(duì)應(yīng)的自動(dòng)配置類( 注解@Configuration 的類)。
CacheConfigurations 相關(guān)代碼如下。
final class CacheConfigurations {
private static final Map> MAPPINGS;
//定義 CacheType 5@Conf iguration 之間的對(duì)應(yīng)關(guān)系
static {
Map> mappings = new EnumMap<>(CacheType.class);
mappings . put(CacheType . GENERIC, GenericCacheConfiguration.class);mappings . put(CacheType . EHCACHE,EhCacheCacheConfiguration. class);
mappings . put (CacheType . HAZELCAST, HazelcastCacheConfiguration. class);
mappings . put(CacheType . INF INISPAN, InfinispanCacheConfiguration.class);
mappings . put (CacheType . JCACHE, JCacheCacheConfiguration. class);
mappings . put (CacheType . COUCHBASE, CouchbaseCacheConfiguration. class);
mappings . put (CacheType. REDIS, RedisCacheConfiguration.class);
mappings . put(CacheType . CAFFEINE, CaffeineCacheConfiguration. class);
mappings . put(CacheType . SIMPLE, SimpleCacheConfiguration.class);
mappings . put(CacheType . NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections . unmodifiableMap(mappings);
//根據(jù) CacheType
型獲得對(duì)應(yīng)的@Configuration 類
static String getConfigurationClass(CacheType cacheType) {
Class> configurationClass = MAPPINGS . get(cacheType);
Assert.state(configurationClass != null, () -> "Unknown cache type ”+
cacheType) ;
return configurationClass . getName();
}
} 經(jīng)過以上步驟,我們會(huì)發(fā)現(xiàn)通過@Import 注解,CacheAutoConfiguration 導(dǎo)入了 CacheType中定義的所有類型的自動(dòng)配置,也就是 Spring Boot 目前支持的緩存類型。而具體會(huì)自動(dòng)配置哪種類型的緩存,還需要看導(dǎo)入的自動(dòng)配置類里面的生效條件。
我們以 GenericCacheConfiguration 為例進(jìn)行了解,源代碼如下。
@Configurat ion(proxyBeanMethods = false)
@ConditionalOnBean(Cache . class)
@ConditionalOnMissingBean(CacheManager . class)
@Conditional(CacheCondition. class)
class GenericCacheConfiguration {
@Bean
SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Coll
ection
caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager . setCaches(caches);
return customizers . customize(cacheManager);
}} 在 GenericCacheConfiguration 的注解部分,@ConditionalOnBean 指定當(dāng) Cache 的 Bean存在時(shí)進(jìn)行實(shí)例化操作,@ConditionalOnMissingBean 指定當(dāng) CacheManager 的 Bean 不存在時(shí)進(jìn)行實(shí)例化操作,@Conditional 指定當(dāng)滿足 CacheCondition 指定的條件時(shí)進(jìn)行實(shí)例化操作。
CacheManager 我們前面已經(jīng)介紹過,不再贅述。Cache 是一 個(gè)定義了緩存通用操作的接口,其中定義了緩存名稱獲取、緩存值獲取、清除緩存、添加緩存值等操作。對(duì)應(yīng)的緩存組件或框架實(shí)現(xiàn)該接口,并根據(jù)組件自身的情況提供對(duì)應(yīng)的操作方法實(shí)現(xiàn)。
下面看 CacheCondition 類中定義的條件。
class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome (ConditionContext context, Annot
ated-
TypeMetadata metadata) {
String sourceClass =
if (metadata instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) metadata). getClassName() ;
Condit ionMessage . Builder message = ConditionMessage . forCondition("C
ache" ,
sourceClass);
Environment environment = context . getEnvironment( ) ;
try {
//創(chuàng)建指定環(huán)境的 Binder, 然后綁定屬性到對(duì)象上
BindResult<CacheType> specified = Binder . get( environment) . bind
("spring.
cache. type", CacheType . class);
//如果未綁定,則返回匹配
if (!specified. isBound()) {
return ConditionOutcome . match(message . because("automatic ca
che type"));
//獲取所需的緩存類型
CacheType required = CacheConfigurations . getType(((AnnotationMe
tadata)
metadata) . getClassName());
//如果已綁定,并且綁定的類型與所需的緩存類型相同,則返回匹配
if (specified.get() == required) {return Conditi onOutcome . match(message. because(specified. get
() +”cache type"));
} catch (BindException ex) {
//其他情況則返回不匹配
return ConditionOutcome . noMatch(message . because("unknown cache typ
e"));
}
}CacheCondition 的核心邏輯就是首先通過 Binder 進(jìn)行指定屬性和類的綁定,然后通過綁定結(jié)果( BindResult)進(jìn)行判斷:如果判斷結(jié)果是未綁定,則直接返回條件匹配;否則,判斷綁定的緩存類型與所需的緩存類型是否相等,如果相等則返回條件匹配;其他情況則返回條件不匹配。
當(dāng) GenericCacheConfiguration 滿足注解指定的條件后,便會(huì)通過 cacheManager 方法進(jìn)行SimpleCacheManager 類的實(shí)例化操作。首先創(chuàng)建 SimpleCacheManager 對(duì)象,然后將緩存 集 合 設(shè) 置 到 對(duì) 象 中 , 最 后 通 過 CacheManagerCustomizers 的 customize 方 法 對(duì)SimpleCacheManager 進(jìn)行定制化處理。
SimpleCacheManager 類是接口 CacheManager 的一個(gè)實(shí)現(xiàn)類,通過集合來實(shí)現(xiàn)緩存功能,源代碼如下。
public class SimpleCacheManager extends AbstractCacheManager {
private Collection extends Cache> caches = Collections . emptySet();
//設(shè)置緩存集合
public void setCaches (Collection extends Cache> caches) {
this.caches = caches;
//獲取緩存集合
@Override
protected Collection extends Cache> loadCaches() {
return this. caches;
}
}通過以上代碼可以看出,SimpleCacheManager 的實(shí)現(xiàn)極其簡單, 就是基于 Cache 的集合來實(shí)現(xiàn)的,它提供了設(shè)置緩存集合和獲取緩存集合的方法。同樣,由于實(shí)現(xiàn)比較簡單,它往往被用于測試環(huán)境和簡單緩存場景中。
上面我們以 GenericCacheConfiguration 為例講解了@Import 引入的緩存組件配置,關(guān)于其他的類型緩存注解的配置就不再一-講解了。
下 我 繼 看 @Ilmport 的
CacheManagerEntityManagerFactoryDependsOnPostProcess-or類。該類同樣為 CacheAutoConfiguration 的內(nèi)部類。
@ConditionalOnClass(LocalContainerEnt ityManagerFactoryBean. class)
@Conditiona lOnBean(AbstractEntityManagerFactoryBean.class)
static class CacheManagerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
CacheManagerEntityManagerFactoryDependsOnPostProcessor() {
super("cacheManager");}
}該 類 實(shí) 現(xiàn) 了
EntityManagerFactoryDependsOnPostProcessor, 本質(zhì)上是BeanFactoryPost-Processor 的 一 個(gè) 實(shí) 現(xiàn) 類 。當(dāng) classpath中存在LocalContainerEntityManagerFactoryBean類和實(shí)現(xiàn)了抽象類AbstractEntityManagerFactoryBean 的類的 Bean 時(shí),才會(huì)進(jìn)行實(shí)例化操作。
在該類的構(gòu)造方法中調(diào)用父類構(gòu)造方法并傳遞值"cacheManager”。因此,動(dòng)態(tài)聲明了所有類型為 EntityManagerFactory 的 Bean 都必須依賴于名稱為 cacheManager 的 Bean。

最后,我們看一下 CacheAutoConfiguration 中其余的代碼。
//實(shí)例化 CacheManagerCus tomizers
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider> customizers) {
return new CacheManagerCustomizers(
customi zers . orderedStream(). collect(Collectors . toList()));
} cacheManagerCustomizers 方法初始化了 CacheManagerCustomizers 對(duì)象的 Bean,主要是 將 容 器 中 存 在 的 一 一 個(gè) 或 多 個(gè) CacheManagerCustomizer 的 Bean 組 件 包 裝 為CacheManager-Customizers,并將 Bean 注入容器。
//實(shí)例化 CacheManagerVal idator
@Bean
public CacheManagerValidator cacheAutoConfigurat ionValidator(CachePropert
ies-
cachePropert
ies,
objectProvid
er cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
// CacheManagerVal idator 的具體定義,用于檢查并拋出有意義的異常 static class
CacheManagerValidator implements InitializingBean {private final CacheProperties cacheProperties;
private final objectProvider cacheManager;
CacheManagerValidator(CacheProperties cacheProperties, objectProviderkCac
he-
Manager> cacheManager) {
this. cacheProperties = cacheProperties;
this. cacheManager = cacheManager;
}
@Override
public void afterPropertiesSet() {
Assert . notNull(this . cacheManager . getIfAvailable(),
) -> "No cache manager could be auto- configured, check
your configuration (caching ”+ "type is” + this. cacheProperties .getType
() + "')");
}
}
cacheAutoConfigurationValidator 方法初始化了 CacheManagerValidator 的 Bean,該 Bean用于確保容器中存在一個(gè) CacheManager 對(duì)象, 以達(dá)到緩存機(jī)制可以繼續(xù)被配置和使用的目的,同時(shí)該 Bean 也用來提供有意義的異常聲明。
至此關(guān)于 Spring Boot 中 cache 的 CacheAutoConfiguration 自動(dòng)配置講解完畢,隨后我們會(huì)繼續(xù)講一下Spring Boot 中默認(rèn)的自動(dòng)配置。
本文給大家講解的內(nèi)容是SpringBootCache源碼解析:Cache自動(dòng)配置
下篇文章給大家講解的是SpringBootCache源碼解析:默認(rèn)Cache配置;
覺得文章不錯(cuò)的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;
感謝大家的支持!
本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學(xué)習(xí)更多的話可以到微信公眾號(hào)里找我,我等你哦。
