SpringBoot Cache緩存概念講解
Spring 3.1中引入了基于注解的Cache的支持,在spring-context包中定義了org.springframework.cache.CacheManager和org.springframework.cache.Cache接口,用來(lái)統(tǒng)一不同的緩存的技術(shù)。
CacheManager是Spring提供的各種緩存技術(shù)管理的抽象接口,而Cache接口包含緩存的增加、刪除、讀取等常用操作。針對(duì)CacheManager,Spring又提供了多種實(shí)現(xiàn),比如基于Collection來(lái)實(shí)現(xiàn)的SimpleCacheManager、基于ConcurrentHashMap實(shí)現(xiàn)的ConcurrentMapCacheManager、基于EhCache實(shí)現(xiàn)的EhCacheCacheManager和基于JCache標(biāo)準(zhǔn)實(shí)現(xiàn)的JCacheCacheManager等。
Spring Cache提供了@CacheConfig、@Cacheable、@CachePut、@CacheEvict等注解來(lái)完成緩存的透明化操作,相關(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的基本作用的和定義之后,下面來(lái)看在Spring Boot中是如何對(duì)Cache進(jìn)行自動(dòng)配置的
Cache自動(dòng)配置
在Spring Boot中,關(guān)于Cache的默認(rèn)自動(dòng)配置類只有CacheAutoConfiguration,主要用于緩存抽象的自動(dòng)配置,當(dāng)通過(guò)@EnableCaching啟用緩存機(jī)制時(shí),根據(jù)情況可創(chuàng)建CacheManager。對(duì)于緩存存儲(chǔ)可以通過(guò)配置自動(dòng)檢測(cè)或明確指定。CacheAutoConfiguration同樣在META-INF/spring.factories文件中配置注冊(cè)。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
下面先來(lái)看CacheAutoConfiguration類的注解部分代碼實(shí)現(xiàn)。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
...
}
1、@ConditionalOnClass 指定需要在classpath下存在CacheManager類。
2、@ConditionalOnBean 指定需要存在CacheAspectSupport的Bean時(shí)才生效,換句話說(shuō),就是需要在使用了@EnableCaching時(shí)才有效。這是因?yàn)樵撟⒔怆[式的導(dǎo)致了CacheInterceptor對(duì)應(yīng)的Bean的初始化,而CacheInterceptor為CacheAspectSupport的子類。
3、@ConditionalOnMissingBean指定名稱為cacheResolver的CacheManager對(duì)象不存在時(shí)生效。@EnableConfigurationProperties加載緩存的CacheProperties配置項(xiàng),配置前綴為spring.cache。
4、@EnableConfigurationProperties加載緩存的CacheProperties配置項(xiàng),配置前綴為spring.cache。@AutoConfigureAfter指定該自動(dòng)配置必須在緩存數(shù)據(jù)基礎(chǔ)組件自動(dòng)配置之后進(jìn)行,這里包括Couchbase、Hazelcast、HibernateJpa和Redis的自動(dòng)配置。
5、@Import導(dǎo)入CacheConfigurationImportSelector,其實(shí)是導(dǎo)入符合條件的Spring Cache使用的各類基礎(chǔ)緩存框架(或組件)的配置。
ImportSelector
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
導(dǎo)入類的獲取是通過(guò)實(shí)現(xiàn)ImportSelector接口來(lái)完成的,具體獲取步驟位于selectImports方法中。該方法中,首先獲取枚舉類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)存基本的簡(jiǎn)單緩存
SIMPLE,
// 不支持緩存
NONE
}
枚舉類CacheType中定義了以上支持的緩存類型,而且上面的緩存類型默認(rèn)是按照優(yōu)先級(jí)從前到后的順序排列的。selectImports方法中,當(dāng)獲取CacheType中定義的緩存類型數(shù)組之后,遍歷該數(shù)組并通過(guò)CacheConfigurations的getConfigurationClass方法獲得每種類型緩存對(duì)應(yīng)的自動(dòng)配置類(注解@Configuration的類)。CacheConfigurations相關(guān)代碼如下。
final class CacheConfigurations {
private static final Map<CacheType, Class<?>> MAPPINGS;
// 定義CacheType與@Configuration之間的對(duì)應(yīng)關(guān)系
static {
Map<CacheType, Class<?>> 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.INFINISPAN, 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();
}
...
}我們會(huì)發(fā)現(xiàn)通過(guò)@Import注解,CacheAutoConfiguration導(dǎo)入了CacheType中定義的所有類型的自動(dòng)配置,也就是Spring Boot目前支持的緩存類型。而具體會(huì)自動(dòng)配置哪種類型的緩存,還需要看導(dǎo)入的自動(dòng)配置類里面的生效條件。
GenericCacheConfiguration
// 實(shí)例化CacheManagerCustomizers
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers(
customizers.orderedStream().collect(Collectors.toList()));
}
cacheManagerCustomizers方法初始化了CacheManagerCustomizers對(duì)象的Bean,主要是將容器中存在的一個(gè)或多個(gè)CacheManagerCustomizer的Bean組件包裝為CacheManager-Customizers,并將Bean注入容器。
CacheManagerValidator
cacheAutoConfigurationValidator方法初始化了CacheManagerValidator的Bean,該Bean用于確保容器中存在一個(gè)CacheManager對(duì)象,以達(dá)到緩存機(jī)制可以繼續(xù)被配置和使用的目的,同時(shí)該Bean也用來(lái)提供有意義的異常聲明。
// 實(shí)例化CacheManagerValidator
@Bean
public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties-
cacheProperties,
ObjectProvider<CacheManager> cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
}
// CacheManagerValidator的具體定義,用于檢查并拋出有意義的異常
static class CacheManagerValidator implements InitializingBean {
private final CacheProperties cacheProperties;
private final ObjectProvider<CacheManager> cacheManager;
CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<Cache-
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() + "')");
}
}
Redis Cache
RedisCacheConfiguration
我們以RedisCacheConfiguration為例進(jìn)行分析。源碼如下
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
@Bean
RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return cacheManagerCustomizers.customize(builder.build());
}
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
CacheProperties cacheProperties,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
ClassLoader classLoader) {
return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
}
private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
CacheProperties cacheProperties, ClassLoader classLoader) {
Redis redisProperties = cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
.defaultCacheConfig();
config = config.serializeValuesWith(
SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}@ConditionalOnClass:當(dāng) RedisConnectionFactory 在存在時(shí)
@AutoConfigureAfter:在 RedisAutoConfiguration 之后加載
@ConditionalOnBean:RedisConnectionFactory 實(shí)例化之后
@ConditionalOnMissingBean:當(dāng) CacheManager 的 Bean 不存在時(shí)進(jìn)行實(shí)例化操作
@Conditional:滿足 CacheCondition 條件時(shí),進(jìn)行實(shí)例化操作
CacheCondition
class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, Annotated-
TypeMetadata metadata) {
String sourceClass = "";
if (metadata instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) metadata).getClassName();
}
ConditionMessage.Builder message = ConditionMessage.forCondition("Cache",
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 cache type"));
}
// 獲取所需的緩存類型
CacheType required = CacheConfigurations.getType(((AnnotationMetadata)
metadata).getClassName());
// 如果已綁定,并且綁定的類型與所需的緩存類型相同,則返回匹配
if (specified.get() == required) {
return ConditionOutcome.match(message.because(specified.get() + " cache type"));
}
} catch (BindException ex) {
}
// 其他情況則返回不匹配
return ConditionOutcome.noMatch(message.because("unknown cache type"));
}
}CacheCondition的核心邏輯就是首先通過(guò)Binder進(jìn)行指定屬性和類的綁定,然后通過(guò)綁定結(jié)果(BindResult)進(jìn)行判斷:如果判斷結(jié)果是未綁定,則直接返回條件匹配;否則,判斷綁定的緩存類型與所需的緩存類型是否相等,如果相等則返回條件匹配;其他情況則返回條件不匹配。
RedisCacheManager
1、管理多個(gè) RedisCache
2、每個(gè) RedisCache 包含一個(gè) name
3、每個(gè) RedisCache 可以包含一個(gè) RedisCacheConfiguration(可以有默認(rèn)配置)
4、支持配置是否動(dòng)態(tài)增加 Cache
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
}
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
private final boolean allowInFlightCacheCreation;
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList();
Iterator var2 = this.initialCacheConfiguration.entrySet().iterator();
while(var2.hasNext()) {
Entry<String, RedisCacheConfiguration> entry = (Entry)var2.next();
caches.add(this.createRedisCache((String)entry.getKey(), (RedisCacheConfiguration)entry.getValue()));
}
return caches;
}
protected RedisCache getMissingCache(String name) {
return this.allowInFlightCacheCreation ? this.createRedisCache(name, this.defaultCacheConfig) : null;
}
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap(this.getCacheNames().size());
this.getCacheNames().forEach((it) -> {
RedisCache cache = (RedisCache)RedisCache.class.cast(this.lookupCache(it));
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
});
return Collections.unmodifiableMap(configurationMap);
}
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new RedisCache(name, this.cacheWriter, cacheConfig != null ? cacheConfig : this.defaultCacheConfig);
}
}RedisCache
public class RedisCache extends AbstractValueAdaptingCache {
private static final byte[] BINARY_NULL_VALUE;
private final String name;
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration cacheConfig;
private final ConversionService conversionService;
}
RedisCache 的特點(diǎn)
1、key 支持非 String 類型,比如 Map 和 Collection
2、RedisCacheWriter 是更底層的實(shí)現(xiàn),RedisCache 對(duì) RedisCacheWriter 進(jìn)行封裝。
3、通過(guò) ConversionService 對(duì) key 進(jìn)行轉(zhuǎn)換
RedisCacheWriter
通過(guò) RedisConnectionFactory 取一個(gè) RedisConnection,然后執(zhí)行命令。putIfAbsent 支持分布式鎖。
到此這篇關(guān)于SpringBoot Cache緩存概念講解的文章就介紹到這了,更多相關(guān)SpringBoot Cache內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven實(shí)現(xiàn)項(xiàng)目構(gòu)建工具
本文主要介紹了Maven實(shí)現(xiàn)項(xiàng)目構(gòu)建工具,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
Java語(yǔ)言之LinkedList和鏈表的實(shí)現(xiàn)方法
LinkedList是由傳統(tǒng)的鏈表數(shù)據(jù)結(jié)構(gòu)演變而來(lái)的,鏈表是一種基本的數(shù)據(jù)結(jié)構(gòu),它可以動(dòng)態(tài)地增加或刪除元素,下面這篇文章主要給大家介紹了關(guān)于Java語(yǔ)言之LinkedList和鏈表的實(shí)現(xiàn)方法,需要的朋友可以參考下2023-05-05
解析spring-security權(quán)限控制和校驗(yàn)的問(wèn)題
這篇文章主要介紹了解析spring-security權(quán)限控制和校驗(yàn)的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
mybatis?實(shí)體類字段大小寫問(wèn)題?字段獲取不到值的解決
這篇文章主要介紹了mybatis?實(shí)體類字段大小寫問(wèn)題?字段獲取不到值的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Thread線程的基礎(chǔ)知識(shí)及常見疑惑點(diǎn)總結(jié)
在本篇內(nèi)容里小編給大家分享的是關(guān)于Thread線程的基礎(chǔ)知識(shí)及常見疑惑點(diǎn),對(duì)此有學(xué)習(xí)需求的朋友們可以學(xué)習(xí)參考下。2019-05-05
Springboot+Spring Security實(shí)現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
本文主要介紹了Springboot+Spring Security實(shí)現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

