解決RedisTemplate的key默認序列化器的問題
redis的客戶端換成了spring-boot-starter-data-redis,碰到了一個奇怪的問題,
在同一個方法中
1.先hset,再hget,正常獲得數(shù)據(jù)。
在不同的方法中 先hset,再hget獲取不到數(shù)據(jù),通過redis的monitor監(jiān)控發(fā)現(xiàn)了命令的問題:

實際我的key為JK_HASH:csrk,hashkey為user,但是根據(jù)上圖所示,實際執(zhí)行的命令多了好多其他字符,這是什么原因呢?
在服務(wù)器端先確認發(fā)現(xiàn)實際有這個Hash,通過hset可以得到正確的數(shù)據(jù),所以第一次執(zhí)行hset的時候命令是正常的,問題可能出現(xiàn)在hget上面,先打開源碼看一下
@SuppressWarnings("unchecked")
public HV get(K key, Object hashKey) {
final byte[] rawKey = rawKey(key);
final byte[] rawHashKey = rawHashKey(hashKey);
byte[] rawHashValue = execute(new RedisCallback<byte[]>() {
public byte[] doInRedis(RedisConnection connection) {
return connection.hGet(rawKey, rawHashKey);
}
}, true);
return (HV) deserializeHashValue(rawHashValue);
}
從這里可以看到實際上傳給redis的都是byte數(shù)據(jù),而byte數(shù)組是rawKey和rawHashKey生成的,先看下rawKey方法
@SuppressWarnings("unchecked")
byte[] rawKey(Object key) {
Assert.notNull(key, "non null key required");
if (keySerializer() == null && key instanceof byte[]) {
return (byte[]) key;
}
return keySerializer().serialize(key);
}
然后進一步跟蹤keySerializer()方法
RedisSerializer keySerializer() {
return template.getKeySerializer();
}
public RedisSerializer<?> getKeySerializer() {
return keySerializer;
}
最后跟蹤到是RedisTemplate中的屬性keySerializer導(dǎo)致的,而通過打印keySerializer的class發(fā)現(xiàn) 默認使用的是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,但它是如何進行初始化的呢,默認的構(gòu)造函數(shù)中并沒有對該屬性進行初始化。
根據(jù)RedisTemplate的類關(guān)系發(fā)現(xiàn)它是繼承RedisAccessor的,而此類是實現(xiàn)的org.springframework.beans.factory.InitializingBean接口,這個接口有個特性,凡是繼承該接口的類,在初始化bean的時候會執(zhí)行afterPropertiesSet方法。
而afterPropertiesSet方法中,確實對keySerializer進行了初始化:
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}
在這里可以看到默認使用的正是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,而問題正在這里,通過查詢可以發(fā)現(xiàn)序列化器有這些,而在這里我們需要使用的是StringRedisSerializer

加入如下代碼:
@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
this.redisTemplate = redisTemplate;
}
重新進行測試,方法1hset,方法2hget,方法2能拿到正確的數(shù)據(jù),完畢。
補充:redisTemplate取對象時反序列化失敗
錯誤:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized field
注意:反序列化時要保證實體對象有一個無參構(gòu)造函數(shù),否則反序列化也會失敗
json序列化是根據(jù)set和get方法來序列化字段的,如果方法正好有set和get開頭但沒有對應(yīng)field,那么反序列化就會失敗。
可以在實體類加上注解@JsonIgnoreProperties(ignoreUnknown = true)忽略實體中沒有對應(yīng)的json的key值,或者在set方法上加上@JsonIgnore注解,如果是用mongoDb數(shù)據(jù)庫,第二種方法那么就不大適用了,總不能去ObjectId類操作,這是修改別人的源代碼。
以下是redisTemplate在springboot2.x里面存取對象的配置,等同于第一種方法
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
//Use Jackson 2Json RedisSerializer to serialize and deserialize the value of redis (default JDK serialization)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//將類名稱序列化到j(luò)son串中,去掉會導(dǎo)致得出來的的是LinkedHashMap對象,直接轉(zhuǎn)換實體對象會失敗
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//設(shè)置輸入時忽略JSON字符串中存在而Java對象實際沒有的屬性
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//Use String RedisSerializer to serialize and deserialize the key value of redis
RedisSerializer redisSerializer = new StringRedisSerializer();
//key
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
//value
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
application.properties文件添加如下,springboot2.x連接池用的是lettuce
spring.redis.host=localhost spring.redis.password= # 連接超時時間(毫秒) spring.redis.timeout=3000ms # Redis默認情況下有16個分片,這里配置具體使用的分片,默認是0 spring.redis.database=0 # 連接池最大連接數(shù)(使用負值表示沒有限制) 默認 8 spring.redis.lettuce.pool.max-active=8 # 連接池最大阻塞等待時間(使用負值表示沒有限制) 默認 -1 spring.redis.lettuce.pool.max-wait=-1 # 連接池中的最大空閑連接 默認 8 spring.redis.lettuce.pool.max-idle=8 # 連接池中的最小空閑連接 默認 0 spring.redis.lettuce.pool.min-idle=0
添加數(shù)據(jù)進redis
@Test
public void testRedis() {
Headset headset = new Headset();
redisTemplate.opsForValue().set("test:123", headset);
System.out.println(redisTemplate.opsForValue().get("test:123"));
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
- RedisTemplate默認序列化方式顯示中文亂碼的解決
- RedisTemplate序列化設(shè)置的流程和具體步驟
- redis redistemplate序列化對象配置方式
- 配置redis的序列化,注入RedisTemplate方式
- Spring?boot?RedisTemplate?序列化服務(wù)化配置方式
- Springboot下RedisTemplate的兩種序列化方式實例詳解
- Springboot?引入?Redis?并配置序列化并封裝RedisTemplate?
- Redis之RedisTemplate配置方式(序列和反序列化)
- Spring的RedisTemplate的json反序列泛型丟失問題解決
相關(guān)文章
SpringCloud全面解析@FeignClient標識接口的過程
這篇文章主要介紹了SpringCloud全面解析@FeignClient標識接口的過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Java高并發(fā)編程之CAS實現(xiàn)無鎖隊列代碼實例
這篇文章主要介紹了Java高并發(fā)編程之CAS實現(xiàn)無鎖隊列代碼實例,在多線程操作中,我們通常會添加鎖來保證線程的安全,那么這樣勢必會影響程序的性能,那么為了解決這一問題,于是就有了在無鎖操作的情況下依然能夠保證線程的安全,需要的朋友可以參考下2023-12-12
IDEA修改idea64.exe.vmoptions文件以及解決coding卡頓問題
IDEA修改idea64.exe.vmoptions文件以及解決coding卡頓問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
springboot集成RestTemplate及常見的用法說明
這篇文章主要介紹了springboot集成RestTemplate及常見的用法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
mybatis-plus之如何根據(jù)數(shù)據(jù)庫主鍵定義字段類型
這篇文章主要介紹了mybatis-plus之如何根據(jù)數(shù)據(jù)庫主鍵定義字段類型問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
如何使用Spring Validation優(yōu)雅地校驗參數(shù)
這篇文章主要介紹了如何使用Spring Validation優(yōu)雅地校驗參數(shù),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
Java實現(xiàn)的生成二維碼統(tǒng)計掃描次數(shù)并轉(zhuǎn)發(fā)到某個地址功能詳解
這篇文章主要介紹了Java實現(xiàn)的生成二維碼統(tǒng)計掃描次數(shù)并轉(zhuǎn)發(fā)到某個地址功能,可實現(xiàn)生成帶統(tǒng)計功能的二維碼,涉及java二維碼的生成、參數(shù)傳遞、解析等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07

