在使用Spring-data-redis 时, 很多文章都推荐使用GenericJackson2JsonRedisSerializer 作为redisTemplate 的序列化执行器,
然而,在遇到类中有 LocalDateTime 等类型时, 反序列化会有问题,大概错误提示为:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `java.time.LocalDateTime` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
比如这个类:
存在redis 里面的值大概长这样,可以看到 LocalDateTime的内部字段都当作json数据存起来了,反序列化时肯定找不到
构造器。
解决办法:
1、先添加依赖,(注意:在设置了spring boot的parent的情况下不需要指定具体的版本,也不建议指定某个具体版本)
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
2、自定义ObjectMapper ,然后注入到GenericJackson2JsonRedisSerializer ,就可以直接使用了。
ObjectMapper om = new ObjectMapper();
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
om.registerModule((new SimpleModule())
.addSerializer(new NullValueSerializer()));
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(om);
以下是原始文章,已作废!
不知道以前自己在想什么, 做出这种脱了裤子放屁的操作。
2、然后我们依赖GenericJackson2JsonRedisSerializer 新写一个类:
package com.example.demoredis.util;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.support.NullValue;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.io.IOException;
public class GenericJackson2JsonRedisSerializerEx implements RedisSerializer<Object> {
protected GenericJackson2JsonRedisSerializer serializer = null;
public GenericJackson2JsonRedisSerializerEx() {
ObjectMapper om = new ObjectMapper();
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
om.registerModule((new SimpleModule())
.addSerializer(new NullValueSerializer()));
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
this.serializer = new GenericJackson2JsonRedisSerializer(om);
}
public GenericJackson2JsonRedisSerializerEx(ObjectMapper om) {
this.serializer = new GenericJackson2JsonRedisSerializer(om);
}
@Override
public byte[] serialize(Object o) throws SerializationException {
return serializer.serialize(o);
}
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
return serializer.deserialize(bytes);
}
protected class NullValueSerializer extends StdSerializer<NullValue> {
private static final long serialVersionUID = 1999052150548658807L;
private final String classIdentifier="@class";
NullValueSerializer() {
super(NullValue.class);
}
public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeStringField(this.classIdentifier, NullValue.class.getName());
jgen.writeEndObject();
}
}
}
3、最后这么使用:
@Bean
RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
def template = new RedisTemplate()
template.setConnectionFactory(redisConnectionFactory)
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializerEx())
template.setEnableDefaultSerializer(true)
return template
}
4、运行成功,redis里面的值长这样:
这里不得不吐槽一下GenericJackson2JsonRedisSerializer 的作者,
里面的内部类和域都是private 的,改源码限制颇多,虽然原始类很简单,copy一份源码出来稍加改造就行
但是对于懒人来说,连代码都懒得copy (虽然万不得已还是考了他的内部类,万恶的private)
参考: