SpringBoot Cache緩存概念講解
Spring 3.1中引入了基于注解的Cache的支持,在spring-context包中定義了org.springframework.cache.CacheManager和org.springframework.cache.Cache接口,用來統(tǒng)一不同的緩存的技術(shù)。
CacheManager是Spring提供的各種緩存技術(shù)管理的抽象接口,而Cache接口包含緩存的增加、刪除、讀取等常用操作。針對CacheManager,Spring又提供了多種實現(xiàn),比如基于Collection來實現(xiàn)的SimpleCacheManager、基于ConcurrentHashMap實現(xiàn)的ConcurrentMapCacheManager、基于EhCache實現(xiàn)的EhCacheCacheManager和基于JCache標準實現(xiàn)的JCacheCacheManager等。
Spring Cache提供了@CacheConfig、@Cacheable、@CachePut、@CacheEvict等注解來完成緩存的透明化操作,相關(guān)功能如下。
- @CacheConfig:用于類上,緩存一些公共設(shè)置。
- @Cacheable:用于方法上,根據(jù)方法的請求參數(shù)對結(jié)果進行緩存,下次讀取時直接讀取緩存內(nèi)容。
- @CachePut:用于方法上,能夠根據(jù)方法的請求參數(shù)對其結(jié)果進行緩存,和@Cacheable不同的是,它每次都會觸發(fā)真實方法的調(diào)用
- @CacheEvict:用于方法上,清除該方法的緩存,用在類上清除整個類的方法的緩存。在了解了Spring Cache的基本作用的和定義之后,下面來看在Spring Boot中是如何對Cache進行自動配置的
Cache自動配置
在Spring Boot中,關(guān)于Cache的默認自動配置類只有CacheAutoConfiguration,主要用于緩存抽象的自動配置,當通過@EnableCaching啟用緩存機制時,根據(jù)情況可創(chuàng)建CacheManager。對于緩存存儲可以通過配置自動檢測或明確指定。CacheAutoConfiguration同樣在META-INF/spring.factories文件中配置注冊。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
下面先來看CacheAutoConfiguration類的注解部分代碼實現(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時才生效,換句話說,就是需要在使用了@EnableCaching時才有效。這是因為該注解隱式的導致了CacheInterceptor對應(yīng)的Bean的初始化,而CacheInterceptor為CacheAspectSupport的子類。
3、@ConditionalOnMissingBean指定名稱為cacheResolver的CacheManager對象不存在時生效。@EnableConfigurationProperties加載緩存的CacheProperties配置項,配置前綴為spring.cache。
4、@EnableConfigurationProperties加載緩存的CacheProperties配置項,配置前綴為spring.cache。@AutoConfigureAfter指定該自動配置必須在緩存數(shù)據(jù)基礎(chǔ)組件自動配置之后進行,這里包括Couchbase、Hazelcast、HibernateJpa和Redis的自動配置。
5、@Import導入CacheConfigurationImportSelector,其實是導入符合條件的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; } }
導入類的獲取是通過實現(xiàn)ImportSelector接口來完成的,具體獲取步驟位于selectImports方法中。該方法中,首先獲取枚舉類CacheType中定義的緩存類型數(shù)據(jù),CacheType中定義支持的緩存類型如下。
// 支持的緩存類型(按照優(yōu)先級定義) public enum CacheType { // 使用上下文中的Cache Bean進行通用緩存 GENERIC, // JCache(JSR-107)支持的緩存 JCACHE, // EhCache支持的緩存 EHCACHE, // Hazelcast支持的緩存 HAZELCAST, // Infinispan支持的緩存 INFINISPAN, // Couchbase支持的緩存 COUCHBASE, // Redis支持的緩存 REDIS, // Caffeine支持的緩存 CAFFEINE, // 內(nèi)存基本的簡單緩存 SIMPLE, // 不支持緩存 NONE }
枚舉類CacheType中定義了以上支持的緩存類型,而且上面的緩存類型默認是按照優(yōu)先級從前到后的順序排列的。selectImports方法中,當獲取CacheType中定義的緩存類型數(shù)組之后,遍歷該數(shù)組并通過CacheConfigurations的getConfigurationClass方法獲得每種類型緩存對應(yīng)的自動配置類(注解@Configuration的類)。CacheConfigurations相關(guān)代碼如下。
final class CacheConfigurations { private static final Map<CacheType, Class<?>> MAPPINGS; // 定義CacheType與@Configuration之間的對應(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類型獲得對應(yīng)的@Configuration類 static String getConfigurationClass(CacheType cacheType) { Class<?> configurationClass = MAPPINGS.get(cacheType); Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType); return configurationClass.getName(); } ... }
我們會發(fā)現(xiàn)通過@Import注解,CacheAutoConfiguration導入了CacheType中定義的所有類型的自動配置,也就是Spring Boot目前支持的緩存類型。而具體會自動配置哪種類型的緩存,還需要看導入的自動配置類里面的生效條件。
GenericCacheConfiguration
// 實例化CacheManagerCustomizers @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers( ObjectProvider<CacheManagerCustomizer<?>> customizers) { return new CacheManagerCustomizers( customizers.orderedStream().collect(Collectors.toList())); }
cacheManagerCustomizers方法初始化了CacheManagerCustomizers對象的Bean,主要是將容器中存在的一個或多個CacheManagerCustomizer的Bean組件包裝為CacheManager-Customizers,并將Bean注入容器。
CacheManagerValidator
cacheAutoConfigurationValidator方法初始化了CacheManagerValidator的Bean,該Bean用于確保容器中存在一個CacheManager對象,以達到緩存機制可以繼續(xù)被配置和使用的目的,同時該Bean也用來提供有意義的異常聲明。
// 實例化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為例進行分析。源碼如下
@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:當 RedisConnectionFactory 在存在時
@AutoConfigureAfter:在 RedisAutoConfiguration 之后加載
@ConditionalOnBean:RedisConnectionFactory 實例化之后
@ConditionalOnMissingBean:當 CacheManager 的 Bean 不存在時進行實例化操作
@Conditional:滿足 CacheCondition 條件時,進行實例化操作
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,然后綁定屬性到對象上 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的核心邏輯就是首先通過Binder進行指定屬性和類的綁定,然后通過綁定結(jié)果(BindResult)進行判斷:如果判斷結(jié)果是未綁定,則直接返回條件匹配;否則,判斷綁定的緩存類型與所需的緩存類型是否相等,如果相等則返回條件匹配;其他情況則返回條件不匹配。
RedisCacheManager
1、管理多個 RedisCache
2、每個 RedisCache 包含一個 name
3、每個 RedisCache 可以包含一個 RedisCacheConfiguration(可以有默認配置)
4、支持配置是否動態(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 的特點
1、key 支持非 String 類型,比如 Map 和 Collection
2、RedisCacheWriter 是更底層的實現(xiàn),RedisCache 對 RedisCacheWriter 進行封裝。
3、通過 ConversionService 對 key 進行轉(zhuǎn)換
RedisCacheWriter
通過 RedisConnectionFactory 取一個 RedisConnection,然后執(zhí)行命令。putIfAbsent 支持分布式鎖。
到此這篇關(guān)于SpringBoot Cache緩存概念講解的文章就介紹到這了,更多相關(guān)SpringBoot Cache內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java語言之LinkedList和鏈表的實現(xiàn)方法
LinkedList是由傳統(tǒng)的鏈表數(shù)據(jù)結(jié)構(gòu)演變而來的,鏈表是一種基本的數(shù)據(jù)結(jié)構(gòu),它可以動態(tài)地增加或刪除元素,下面這篇文章主要給大家介紹了關(guān)于Java語言之LinkedList和鏈表的實現(xiàn)方法,需要的朋友可以參考下2023-05-05解析spring-security權(quán)限控制和校驗的問題
這篇文章主要介紹了解析spring-security權(quán)限控制和校驗的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03Thread線程的基礎(chǔ)知識及常見疑惑點總結(jié)
在本篇內(nèi)容里小編給大家分享的是關(guān)于Thread線程的基礎(chǔ)知識及常見疑惑點,對此有學習需求的朋友們可以學習參考下。2019-05-05Springboot+Spring Security實現(xiàn)前后端分離登錄認證及權(quán)限控制的示例代碼
本文主要介紹了Springboot+Spring Security實現(xiàn)前后端分離登錄認證及權(quán)限控制的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11