日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring-boot-redis-cache集成总结及源码分析

發布時間:2024/3/13 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring-boot-redis-cache集成总结及源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

緩存

基于spring的Cache進行緩存(基于注解)

Spring緩存支持

聲明式緩存注解

先看看如何使用

1、要想使用Cache緩存,那首先必須的開啟吧

在看看別人的例子

源碼分析

@EnableCaching

由于Cache是通過CacheManager進行管理,在springboot的autoConfig包中,來看看cache的自動配置

cacheManager咋個來的

自定義(解決默認序列化)JdkSerializationRedisSerializer

如何自定義虛擬化


緩存

  • 內存的速度遠遠大于硬盤的速度
  • 緩存主要是在獲取資源方便性能優化的關鍵方面
  • Redis 是緩存數據庫
  • 緩存未命中解決與防止緩存擊穿

基于spring的Cache進行緩存(基于注解)

Spring緩存支持

Spring定義了org.springframework.cache.CacheManager 和 org.springframework.cache.Cache 接口來統一不同緩存技術。

其中CacheManager是Spring提供的各種緩存技術抽象接口,內部使用Cache接口進行緩存的增刪改查操作,我們一般不會直接和Cache打交道。

針對不同的緩存技術,Spring有不同的CacheManager實現類,定義如下表:

CacheManager描述
SimpleCacheManager使用簡單的Collection存儲緩存數據,用來做測試用
ConcurrentMapCacheManager使用ConcurrentMap存儲緩存數據
EhCacheCacheManager使用EhCache作為緩存技術
GuavaCacheManager使用Google Guava的GuavaCache作為緩存技術
JCacheCacheManager使用JCache(JSR-107)標準的實現作為緩存技術,比如Apache Commons JCS
RedisCacheManager使用Redis作為緩存技術【本次使用的】

聲明式緩存注解

Spring提供4個注解來聲明緩存規則,如下表所示:

注解說明
@Cacheable方法執行前先看緩存中是否有數據,如果有直接返回。如果沒有就調用方法,并將方法返回值放入緩存
@CachePut無論怎樣都會執行方法,并將方法返回值放入緩存
@CacheEvict將數據從緩存中刪除
@Caching可通過此注解組合多個注解策略在一個方法上面

@Cacheable 、@CachePut 、@CacheEvict都有value屬性,指定要使用的緩存名稱,而key屬性指定緩存中存儲的鍵。

@EnableCaching 開啟緩存。

@Cacheable

這個注解含義是方法結果會被放入緩存,并且一旦緩存后,下一次調用此方法,會通過key去查找緩存是否存在,如果存在就直接取緩存值,不再執行方法。

這個注解有幾個參數值,定義如下

參數解釋
cacheNames緩存名稱
value緩存名稱的別名
conditionSpring SpEL 表達式,用來確定是否緩存
keySpEL 表達式,用來動態計算key
keyGeneratorBean 名字,用來自定義key生成算法,跟key不能同時用
unlessSpEL 表達式,用來否決緩存,作用跟condition相反
sync多線程同時訪問時候進行同步

在計算key、condition或者unless的值得時候,可以使用到以下的特有的SpEL表達式

表達式解釋
#result表示方法的返回結果
#root.method當前方法
#root.target目標對象
#root.caches被影響到的緩存列表
#root.methodName方法名稱簡稱
#root.targetClass目標類
#root.args[x]方法的第x個參數

@CachePut

該注解在執行完方法后會觸發一次緩存put操作,參數跟@Cacheable一致

@CacheEvict

該注解在執行完方法后會觸發一次緩存evict操作,參數除了@Cacheable里的外,還有個特殊的allEntries, 表示將清空緩存中所有的值。

【引用】吃水不忘挖井人:感謝

在SpringBoot中配置多個cache,實現多個cacheManager靈活切換

好文章,直接學習人家的總結

官方文檔對cache的說明

先看看如何使用

1、要想使用Cache緩存,那首先必須的開啟吧

@EnableCaching

2、由于Cache是通過CacheManager進行管理,在springboot的autoConfig包中,來看看cache的自動配置

CacheManager需要一個cacheManager對象(bean)【看下面源碼分析,有詳細說明】

3、在需要加入緩存的地方加上相關注解即可

@Cacheable(value = "a:obj", key = "#cacheName") @RequestMapping("/k") public Object cacheobjct(String cacheName) {redisTemplate.opsForValue().set("a:wolf:sty01", "wolf-spring-boot");return new User("鄭先生", "正顯示測試緩存", 18); }//我這里就是在controller的方法上加的,最終執行后,在redis就給我創建好了這個緩存數據

4、查看結果

(1)下次在啟動,只要redis里面有這個key的值,直接通過redis加裝,如果沒得話,就通過執行具體邏輯加載,然后在放到redis中

在看看別人的例子

這個就數據連接池、緩存配置類、cacheMangaer全部都自己配,不用springboot底層的默認配置

反過來想,這其實沒必要的。能夠使用底層配置好了的就最好使用配置好了的,除非你不懂。拷貝人家代碼,結果代碼可以正常運行就不管了。俗話說:知其然,必要知其所以然;雖然我不是大牛,但是這就是我學習的方式和初衷;讓學習代碼變得快樂,不枯燥。【2021年2月25日記】--多年后,自己再看看,看看還有啥子心得。

源碼分析

@EnableCaching

通過開啟Cache,然后程序加載掃描所有的類或者方法上加了緩存注解的,然后給他通過AOP切面的方式進行動態代理,讓具體執行的方法具有Cache的功能

由于Cache是通過CacheManager進行管理,在springboot的autoConfig包中,來看看cache的自動配置

先看看傳統基于xml配置的cacheManager

既然用了springboot那他底層是如何生成cacheManager【重點在這,我要一個bean】

cacheManager咋個來的

【源碼源碼】

//他是一共配置類 @Configuration(proxyBeanMethods = false ) //當前系統環境存在這個class的時候才會啟用這個配置類【這些都是springboot用的最多,最基礎的東西,必須要掌握,如果不知道,先學習基礎。不然下面這些注解,會看不懂 @ConditionalOnClass({CacheManager.class}) //當前系統環境存在CacheAspectSupport這個class的時候,配置類啟用做 @ConditionalOnBean({CacheAspectSupport.class}) //如果容器中沒得CacheManager.class類型的bean才進行加載或者beanName=cacheResolver的 @ConditionalOnMissingBean(value = {CacheManager.class},name = {"cacheResolver"} ) //開啟自動加載配置文件:把當前系統環境配置文件的信息,按CacheProperties配置類定義的規則加載到CacheProperties中 //CacheProperties ?@ConfigurationProperties(prefix = "spring.cache")加載已spring.cache開頭的配置信息,然后封裝到CacheProperties對象的屬性中【這個用的也多,很重要】 @EnableConfigurationProperties({CacheProperties.class}) //由于CacheManager需要數據連接,所以這個配置必須在下面這些數據連接配置好了以后在加載 @AutoConfigureAfter({CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) //給容器導入下面這個2個bean @Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class, CacheAutoConfiguration.CacheManagerEntityManagerFactoryDependsOnPostProcessor.class}) public class CacheAutoConfiguration {public CacheAutoConfiguration() {}@Bean@ConditionalOnMissingBeanpublic CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) {return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList()));}@Beanpublic CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager);}static class CacheConfigurationImportSelector implements ImportSelector {CacheConfigurationImportSelector() {}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]);}/** imports的值,緩存配置類org.springframework.boot.autoconfigure.cache.GenericCacheConfigurationorg.springframework.boot.autoconfigure.cache.JCacheCacheConfigurationorg.springframework.boot.autoconfigure.cache.EhCacheCacheConfigurationorg.springframework.boot.autoconfigure.cache.HazelcastCacheConfigurationorg.springframework.boot.autoconfigure.cache.InfinispanCacheConfigurationorg.springframework.boot.autoconfigure.cache.CouchbaseCacheConfigurationorg.springframework.boot.autoconfigure.cache.RedisCacheConfigurationorg.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration 【redis環境引入--它變成了默認了】org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration 【默認】---通過ImportSelector給容器導入SimpleCacheConfiguration這個bean【但是---但是】org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration---由于我當前項目環境引入了redis相關信息及默認有redicConnectionFactory的存在,也就是bean按順序創建的時候,RedisCacheConfiguration這個配置先于SimpleCacheConfiguration加載,那恰好RedisCacheConfiguration滿足條件【看下面源碼剖析】*/return imports;}}static class CacheManagerValidator implements InitializingBean {private final CacheProperties cacheProperties;private final ObjectProvider<CacheManager> cacheManager;CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {this.cacheProperties = cacheProperties;this.cacheManager = cacheManager;}public void afterPropertiesSet() {Assert.notNull(this.cacheManager.getIfAvailable(), () -> {return "No cache manager could be auto-configured, check your configuration (caching type is \'" + this.cacheProperties.getType() + "\')";});}}@ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class})@ConditionalOnBean({AbstractEntityManagerFactoryBean.class})static class CacheManagerEntityManagerFactoryDependsOnPostProcessor extends EntityManagerFactoryDependsOnPostProcessor {CacheManagerEntityManagerFactoryDependsOnPostProcessor() {super(new String[]{"cacheManager"});}} }//在看看SimpleCacheConfiguration的定義嘛 //那想為什么SimpleCacheConfiguration是默認是呢? mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName()); mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class.getName()); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName()); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName()); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName()); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName()); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName()); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName()); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName()); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName()); 【請分別看這些配置類上面的條件:恰恰只有SimpleCacheConfiguration在程序啟動的時候滿足條件】@Configuration(proxyBeanMethods = false ) @ConditionalOnMissingBean({CacheManager.class}) @Conditional({CacheCondition.class}) class SimpleCacheConfiguration {SimpleCacheConfiguration() {}//給容器創建一個ConcurrentMapCacheManager--CacheManager【基于內存,線程安全的緩存管理器】@BeanConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers) {ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();List cacheNames = cacheProperties.getCacheNames();if(!cacheNames.isEmpty()) {cacheManager.setCacheNames(cacheNames);}return (ConcurrentMapCacheManager)cacheManagerCustomizers.customize(cacheManager);} }//【】 @Configuration(proxyBeanMethods = false ) @ConditionalOnClass({RedisConnectionFactory.class})//加入redis以后,springboot底層會通過RedisAutoConfiguration自動配置的@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) //導入了JedisConnectionFactory redisConnectionFactory的連接工廠 @AutoConfigureAfter({RedisAutoConfiguration.class})//然后恰好,這個配置類由在RedisAutoConfiguration配置類完成之后,在進行加載的 @ConditionalOnBean({RedisConnectionFactory.class})//這個時候,在redis自動配置類中,已經完成了RedisConnectionFactory的創建 @ConditionalOnMissingBean({CacheManager.class})//容器中呢,由不存在bean類型為CacheManager的bean @Conditional({CacheCondition.class})//這個是一個條件,也就是滿足CacheCondition里面自定義的條件后,這個配置類才會加載 class RedisCacheConfiguration {RedisCacheConfiguration() {}//重點在這里,這里創建了基于redis的RedisCacheManager//【重點分析】//1、cacheProperties把基于spring.cache的配置文件類加載進來,這里初始化的時候需要用到里面的參數//2、@BeanRedisCacheManager 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(this.determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));List cacheNames = cacheProperties.getCacheNames();if(!cacheNames.isEmpty()) {builder.initialCacheNames(new LinkedHashSet(cacheNames));}if(cacheProperties.getRedis().isEnableStatistics()) {builder.enableStatistics();}redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> {customizer.customize(builder);});//這里最后就返回了基于RedisCacheManagerreturn (RedisCacheManager)cacheManagerCustomizers.customize(builder.build());}//基于配置信息生成了RedisCacheConfiguration配置private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(CacheProperties cacheProperties, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ClassLoader classLoader) {return (org.springframework.data.redis.cache.RedisCacheConfiguration)redisCacheConfiguration.getIfAvailable(() -> {return this.createConfiguration(cacheProperties, classLoader);});}//創建配置信息:主要是cacheManagerCustomizers.customize(builder.build())這個里面會使用配置信息里面設置的屬性private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ClassLoader classLoader) {//拿到redis的配置信息:從配置類里面拿(配置類里面包含redis配置信息)Redis redisProperties = cacheProperties.getRedis();//這里創建了一個默認的RedisCacheConfigurationorg.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();//設置序列化保存值的時候,自用的哪種序列化方式【重點來了--------假如說:我們要自定義保存值或者設置key使用的序列化策略的時候,可以通過修改勒個進行調整嘛】//【再說一次,這里是重點:后面可能由于這種默認序列化策略不好,保存到redis的值,看起不舒服,想通過自定義的策略方式來搞一搞】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;} }

【源碼結束】

自定義(解決默認序列化)JdkSerializationRedisSerializer

JdkSerializationRedisSerializer

來嘛,加入我現在要分析問題;進入redis數據庫查看這個key的值,哪看得懂。所以要改一改默認基于JdkSerializationRedisSerializer的序列化方式

如何自定義虛擬化

思路和方法
1、想辦法給替換RedisCacheConfiguration

config.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)))//把JdkSerializationRedisSerializer替換成自己想實現的虛擬化方式

通過上面源碼里面有,但是反過來想。這個層級太復雜了,而且不好弄。那我直接重新自定義CacheManager,給cacheManager里面保存值的序列化方式進行設置就行了嘛

2、上代碼

//必須要定義個配置類【這個也可以加一些配置類加載策略-根據情況需要】 @Configuration public class CacheManagerConfig {//自定義RedisCacheManager@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {//序列化方式基于StringRedisSerializerRedisSerializer<String> strSerializer = new StringRedisSerializer();//序列化方式基于Jackson2JsonRedisSerializerJackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);//下面是核心//創建一個默認的defaultCacheConfig【這個開始分析springboot默認源碼的時候看到過,直接用。不要去創造】RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()//設置過期時間(redis-Key)--也可以根據需要在注入CacheProperties cacheProperties【配置類信息,通過配置類信息獲取--這樣可以動態替換】//我研究的時候直接研究底層,具體的方式,生產級別使用。曉得以后咋個搞就行了。.entryTtl(Duration.ofDays(1))//【重點】這個就設置自己的序列化方式.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial)).disableCachingNullValues();//這里就是返回一個RedisCacheManager,這里搞這么多就是把你要的數據源給你,配置信息給你。底層管你怎么搞,看看源碼即可。//想不這么寫,隨便。看源碼,最終構建RedisCacheManager的時候,需要什么信息,你提供好就行,只是不通過人家底層的建造者模式(RedisCacheManagerBuilder)這個類來build()//讀源碼是讀思想,學思路。不是說怎么怎么滴【剖析有深有原理就夠了:學到思想到位】RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();return cacheManager;} }//【這里通過建造者模式:搞了一個復雜的對象出來】 public RedisCacheManager build() {Assert.state(this.cacheWriter != null, "CacheWriter must not be null! You can provide one via \'RedisCacheManagerBuilder#cacheWriter(RedisCacheWriter)\'.");RedisCacheWriter theCacheWriter = this.cacheWriter;if(!this.statisticsCollector.equals(CacheStatisticsCollector.none())) {theCacheWriter = this.cacheWriter.withStatisticsCollector(this.statisticsCollector);}RedisCacheManager cm = new RedisCacheManager(theCacheWriter, this.defaultCacheConfiguration, this.initialCaches, this.allowInFlightCacheCreation);cm.setTransactionAware(this.enableTransactions);return cm; }

3、到此為止,自定義的cacheManager就在容器中存在了,然后其他自動加載類,既然容器中有了這個bean,那不會再加載了【先不考了有多個cacheManager存在的情況---實際是允許的】只是我沒記性分析下去了

因為實際的工作場景中,沒那么復雜。

關于配置類里面@ConditionalOnBean({CacheAspectSupport.class})的特別說明

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

總結

以上是生活随笔為你收集整理的spring-boot-redis-cache集成总结及源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。