SpringBoot自定義Redis實(shí)現(xiàn)緩存序列化詳解
剛剛完成了Spring Boot整合Redis進(jìn)行了數(shù)據(jù)的緩存管理,但緩存管理的實(shí)體類(lèi)數(shù)據(jù)使用的是JDK序列化方式,不便于使用可視化管理工具進(jìn)行查看和管理。

接下來(lái)分別針對(duì)基于注解的Redis緩存實(shí)現(xiàn)和基于API的Redis緩存實(shí)現(xiàn)中的數(shù)據(jù)序列化機(jī)制進(jìn)行介紹,并自定義JSON格式的數(shù)據(jù)序列化方式進(jìn)行數(shù)據(jù)緩存管理
1、自定義RedisTemplate
1.1、Redis API默認(rèn)序列化機(jī)制
基于API的Redis緩存實(shí)現(xiàn)是使用RedisTemplate模板進(jìn)行數(shù)據(jù)緩存操作的,這里打開(kāi)RedisTemplate類(lèi),查看該類(lèi)的源碼信息
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
// 聲明了key、value的各種序列化方式,初始值為空
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
...
// 進(jìn)行默認(rèn)序列化方式設(shè)置,設(shè)置為JDK序列化方式
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(
this.classLoader != null ?
this.classLoader : this.getClass().getClassLoader());
}
...
}
...
}從上述RedisTemplate核心源碼可以看出,在RedisTemplate內(nèi)部聲明了緩存數(shù)據(jù)key、value的各種序列化方式,且初始值都為空;在afterPropertiesSet()方法中,判斷如果默認(rèn)序列化參數(shù)defaultSerializer為空,將數(shù)據(jù)的默認(rèn)序列化方式設(shè)置為JdkSerializationRedisSerializer
根據(jù)上述源碼信息的分析,可以得到以下兩個(gè)重要的結(jié)論:
(1)使用RedisTemplate進(jìn)行Redis數(shù)據(jù)緩存操作時(shí),內(nèi)部默認(rèn)使用的是JdkSerializationRedisSerializer序列化方式,所以進(jìn)行數(shù)據(jù)緩存的實(shí)體類(lèi)必須實(shí)現(xiàn)JDK自帶的序列化接口(例如Serializable);
(2)使用RedisTemplate進(jìn)行Redis數(shù)據(jù)緩存操作時(shí),如果自定義了緩存序列化方式defaultSerializer,那么將使用自定義的序列化方式。
另外,在RedisTemplate類(lèi)源碼中,看到的緩存數(shù)據(jù)key、value的各種序列化類(lèi)型都是RedisSerializer。進(jìn)入RedisSerializer源碼查看RedisSerializer支持的序列化方式(進(jìn)入該類(lèi)后,使用Ctrl+Alt+左鍵單擊類(lèi)名查看)

可以看出,RedisSerializer是一個(gè)Redis序列化接口,默認(rèn)有6個(gè)實(shí)現(xiàn)類(lèi),這6個(gè)實(shí)現(xiàn)類(lèi)代表了6種不同的數(shù)據(jù)序列化方式。其中,JdkSerializationRedisSerializer是JDK自帶的,也是RedisTemplate內(nèi)部默認(rèn)使用的數(shù)據(jù)序列化方式,開(kāi)發(fā)者可以根據(jù)需要選擇其他支持的序列化方式(例如JSON方式)
1.2、自定義RedisTemplate序列化機(jī)制
在項(xiàng)目中引入Redis依賴(lài)后,Spring Boot提供的RedisAutoConfiguration自動(dòng)配置會(huì)生效。打開(kāi)RedisAutoConfiguration類(lèi),查看內(nèi)部源碼中關(guān)于RedisTemplate的定義方式
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
...
}從上述RedisAutoConfiguration核心源碼中可以看出,在Redis自動(dòng)配置類(lèi)中,通過(guò)Redis連接工廠RedisConnectionFactory初始化了一個(gè)RedisTemplate;該類(lèi)上方添加了@ConditionalOnMissingBean注解(顧名思義,當(dāng)某個(gè)Bean不存在時(shí)生效),用來(lái)表明如果開(kāi)發(fā)者自定義了一個(gè)名為redisTemplate的Bean,則該默認(rèn)初始化的RedisTemplate不會(huì)生效。
如果想要使用自定義序列化方式的RedisTemplate進(jìn)行數(shù)據(jù)緩存操作,可以參考上述核心代碼創(chuàng)建一個(gè)名為redisTemplate的Bean組件,并在該組件中設(shè)置對(duì)應(yīng)的序列化方式即可
接下來(lái),在項(xiàng)目中創(chuàng)建名為com.lagou.config的包,在該包下創(chuàng)建一個(gè)Redis自定義配置類(lèi)RedisConfig,并按照上述思路自定義名為redisTemplate的Bean組件
@Configuration
public class RedisConfig {
// 自定義RedisTemplate
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 創(chuàng)建一個(gè)JSON格式序列化對(duì)象,對(duì)緩存數(shù)據(jù)的key和value進(jìn)行轉(zhuǎn)換
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 解決查詢(xún)緩存轉(zhuǎn)換異常的問(wèn)題
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 設(shè)置RedisTemplate模板api序列化方式為json
template.setDefaultSerializer(jackson2JsonRedisSerializer);
return template;
}
}通過(guò)@Configuration注解定義了一個(gè)RedisConfig配置類(lèi),并使用@Bean注解注入了一個(gè)默認(rèn)名稱(chēng)為方法名的redisTemplate組件(注意,該Bean組件名稱(chēng)必須是redisTemplate)。在定義的Bean組件中,自定義了一個(gè)RedisTemplate,使用自定義的Jackson2JsonRedisSerializer數(shù)據(jù)序列化方式;在定制序列化方式中,定義了一個(gè)ObjectMapper用于進(jìn)行數(shù)據(jù)轉(zhuǎn)換設(shè)置
1.3、效果測(cè)試

可以看出,執(zhí)行findById()方法正確查詢(xún)出用戶(hù)評(píng)論信息Comment,重復(fù)進(jìn)行同樣的查詢(xún)操作,數(shù)據(jù)庫(kù)只執(zhí)行了一次SQL語(yǔ)句,這說(shuō)明定制的Redis緩存生效。
使用Redis客戶(hù)端可視化管理工具Redis Desktop Manager查看緩存數(shù)據(jù) :

執(zhí)行findById()方法查詢(xún)出用戶(hù)評(píng)論信息Comment正確存儲(chǔ)到了Redis緩存庫(kù)中,且緩存到Redis服務(wù)的數(shù)據(jù)已經(jīng)使用了JSON格式存儲(chǔ)展示,查看和管理也非常方便,說(shuō)明自定義的Redis API模板工具RedisTemplate生效
2、自定義RedisCacheManager
剛剛針對(duì)基于 API方式的RedisTemplate進(jìn)行了自定義序列化方式的改進(jìn),從而實(shí)現(xiàn)了JSON序列化方式緩存數(shù)據(jù),但是這種自定義的RedisTemplate對(duì)于基于注解的Redis緩存來(lái)說(shuō),是沒(méi)有作用的。
接下來(lái),針對(duì)基于注解的Redis緩存機(jī)制和自定義序列化方式進(jìn)行講解
2.1、Redis注解默認(rèn)序列化機(jī)制
打開(kāi)Spring Boot整合Redis組件提供的緩存自動(dòng)配置類(lèi)RedisCacheConfiguration(org.springframework.boot.autoconfigure.cache包下的),查看該類(lèi)的源碼信息,其核心代碼如下
@Configuration
class RedisCacheConfiguration {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(this.determineConfiguration(resourceLoader.getClassLoader()));
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet(cacheNames));
}
return (RedisCacheManager) this.customizerInvoker.customize(builder.build());
}
private org.springframework.data.redis.cache.RedisCacheConfiguration
determineConfiguration(ClassLoader classLoader) {
if (this.redisCacheConfiguration != null) {
return this.redisCacheConfiguration;
} else {
Redis redisProperties = this.cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeValuesWith(SerializationPair.fromSerializer(
new JdkSerializationRedisSerializer(classLoader)));
...
return config;
}
}
}從上述核心源碼中可以看出,同RedisTemplate核心源碼類(lèi)似,RedisCacheConfiguration內(nèi)部同樣通過(guò)Redis連接工廠RedisConnectionFactory定義了一個(gè)緩存管理器RedisCacheManager;同時(shí)定制RedisCacheManager時(shí),也默認(rèn)使用了JdkSerializationRedisSerializer序列化方式。
如果想要使用自定義序列化方式的RedisCacheManager進(jìn)行數(shù)據(jù)緩存操作,可以參考上述核心代碼創(chuàng)建一個(gè)名為cacheManager的Bean組件,并在該組件中設(shè)置對(duì)應(yīng)的序列化方式即可
注意,在Spring Boot 2.X版本中,RedisCacheManager是單獨(dú)進(jìn)行構(gòu)建的。因此,在SpringBoot 2.X版本中,對(duì)RedisTemplate進(jìn)行自定義序列化機(jī)制構(gòu)建后,仍然無(wú)法對(duì)RedisCacheManager內(nèi)部默認(rèn)序列化機(jī)制進(jìn)行覆蓋(這也就解釋了基 于注解的Redis緩存實(shí)現(xiàn)仍然會(huì)使用JDK默認(rèn)序列化機(jī)制的原因),想要基于注解的Redis緩存實(shí)現(xiàn)也使用自定義序列化機(jī)制,需要自定義RedisCacheManager
2.2、自定義RedisCacheManager
在項(xiàng)目的Redis配置類(lèi)RedisConfig中,按照上一步分析的定制方法自定義名為cacheManager的Bean組件
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 分別創(chuàng)建String和JSON格式序列化對(duì)象,對(duì)緩存數(shù)據(jù)key和value進(jìn)行轉(zhuǎn)換
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer(Object.class);
// 解決查詢(xún)緩存轉(zhuǎn)換異常的問(wèn)題
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // 上面注釋過(guò)時(shí)代碼的替代方法
jacksonSerial.setObjectMapper(om);
// 定制緩存數(shù)據(jù)序列化方式及時(shí)效
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1)) // 設(shè)置緩存數(shù)據(jù)的時(shí)效(設(shè)置為了1天)
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(strSerializer)) // 對(duì)當(dāng)前對(duì)象的key使用strSerializer這個(gè)序列化對(duì)象,進(jìn)行轉(zhuǎn)換
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(jacksonSerial)) // 對(duì)value使用jacksonSerial這個(gè)序列化對(duì)象,進(jìn)行轉(zhuǎn)換
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager
.builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}上述代碼中,在RedisConfig配置類(lèi)中使用@Bean注解注入了一個(gè)默認(rèn)名稱(chēng)為方法名的cacheManager組件。在定義的Bean組件中,通過(guò)RedisCacheConfiguration對(duì)緩存數(shù)據(jù)的key和value分別進(jìn)行了序列化方式的定制,其中緩存數(shù)據(jù)的key定制為StringRedisSerializer(即String格式),而value定制為了Jackson2JsonRedisSerializer(即JSON格式),同時(shí)還使用entryTtl(Duration.ofDays(1))方法將緩存數(shù)據(jù)有效期設(shè)置為1天
完成基于注解的Redis緩存管理器RedisCacheManager定制后,可以對(duì)該緩存管理器的效果進(jìn)行測(cè)試(使用自定義序列化機(jī)制的RedisCacheManager測(cè)試時(shí),實(shí)體類(lèi)可以不用實(shí)現(xiàn)序列化接口)
到此這篇關(guān)于SpringBoot自定義Redis實(shí)現(xiàn)緩存序列化詳解的文章就介紹到這了,更多相關(guān)SpringBoot 緩存序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決@Validated注解無(wú)效,嵌套對(duì)象屬性的@NotBlank無(wú)效問(wèn)題
這篇文章主要介紹了解決@Validated注解無(wú)效,嵌套對(duì)象屬性的@NotBlank無(wú)效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
詳解Java并發(fā)編程中的優(yōu)先級(jí)隊(duì)列PriorityBlockingQueue
PriorityBlockingQueue是Java中實(shí)現(xiàn)了堆數(shù)據(jù)結(jié)構(gòu)的線(xiàn)程安全的有界阻塞隊(duì)列。本文將會(huì)深入解讀PriorityBlockingQueue的源碼實(shí)現(xiàn),感興趣的可以了解一下2023-05-05
使用jib插件為Java應(yīng)用構(gòu)建鏡像的方法
這篇文章主要介紹了使用jib插件為Java應(yīng)用構(gòu)建鏡像,要是用戶(hù)本地沒(méi)安裝docker,可以使用jib制作出帶有鏡像的tar文件,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08
JAVA設(shè)置手動(dòng)提交事務(wù),回滾事務(wù),提交事務(wù)的操作
這篇文章主要介紹了JAVA設(shè)置手動(dòng)提交事務(wù),回滾事務(wù),提交事務(wù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
使用Java注解和反射實(shí)現(xiàn)JSON字段自動(dòng)重命名
這篇文章主要介紹了如何使用Java注解和反射實(shí)現(xiàn)JSON字段自動(dòng)重命名,文中通過(guò)代碼示例和圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-08-08
Java?定時(shí)任務(wù)技術(shù)趨勢(shì)詳情
這篇文章主要介紹了Java?定時(shí)任務(wù)技術(shù)趨勢(shì)詳情,定時(shí)任務(wù)是每個(gè)業(yè)務(wù)常見(jiàn)的需求,比如每分鐘掃描超時(shí)支付的訂單,每小時(shí)清理一次數(shù)據(jù)庫(kù)歷史數(shù)據(jù),每天統(tǒng)計(jì)前一天的數(shù)據(jù)并生成報(bào)表等,下文更多相關(guān)資料,需要的小伙伴可以參考一下2022-05-05

