[TOC]
1. key序列化问题 使用spring-data-redis中的redisTemplate存储key-value,然后使用redis-cli去查询时查询不到相应的key。使用keys *时发现redis中key的前缀多了一些字符 \xac\xed\x00\x05t\x00\x0e
问题关键 使用spring-data-redis,默认情况下使用的是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer来进行序列化key
我们看看关键代码 RedisTemplate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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 ; }
解决问题 手动设置key的序列化方式为StringRedisSerializer
看代码
1 2 3 4 5 6 7 8 9 @Bean public RedisTemplate<String, ?> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(factory); RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; }
2. value 序列化问题 我们在使用无参构造GenericJackson2JsonRedisSerializer序列化对象时,序列化出来的值是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { "@class" : "com.xxx.MenuTask" , "taskId" : "1617776640" , "planId" : "10000000" , "createTime" : [ "java.util.Date" , 1500540438240 ], "execDate" : [ "java.util.Date" , 1500480000000 ], "status" : 0 , "totals" : { "@class" : "java.util.HashMap" , "35430d2a56ae508f24a76a68e71b9f53" : 1500 , "75aa86c5a85f65687c95444655c51de9" : 500 , "ab90efa7a0079b88f224a296b211c209" : 1000 } }
这样的json格式使用其他的json序列化时会出现无法解析的错误,如fastjson解析时会抛出异常
1 2 3 4 5 6 7 com.alibaba.fastjson.JSONException: parse error at com.alibaba.fastjson.serializer.DateCodec.cast(DateCodec.java:206 ) at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:142 ) at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:19 ) at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:71 ) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:790 ) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:595 )
主要原因是因为Date格式不正确,应该是一个long类型的数字,而不是一个数组
问题关键 我们来分析一下源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class GenericJackson2JsonRedisSerializer implements RedisSerializer <Object > { private final ObjectMapper mapper; public GenericJackson2JsonRedisSerializer () { this ((String) null ); } public GenericJackson2JsonRedisSerializer (String classPropertyTypeName) { this (new ObjectMapper()); mapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName))); if (StringUtils.hasText(classPropertyTypeName)) { mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName); } else { mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); } } public GenericJackson2JsonRedisSerializer (ObjectMapper mapper) { Assert.notNull(mapper, "ObjectMapper must not be null!" ); this .mapper = mapper; } ... ... }
原因所在地方 // — start — // — end —之间的那段代码 源码的63-69行,这里会将属性的类型写到json中。
解决问题 手动设置一个ObjectMapper,这样redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(new ObjectMapper()));
1 2 3 4 5 6 7 8 9 @Bean public RedisTemplate<String, ?> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(factory); RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(new ObjectMapper())); return redisTemplate; }