spring-data-redis提供了多种serializer策略,默认有七种,当然还可以自定义序列化和反序列化方式。
- JdkSerializationRedisSerializer
- StringRedisSerializer
- JacksonJsonRedisSerializer
- Jackson2JsonRedisSerializer
- GenericJackson2JsonRedisSerializer
- GenericToStringSerializer
- OxmSerializer
//RedisTemplate默认的序列化方式为JdkSerializationRedisSerializer
private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer();
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
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;
}
}
}
一、先看序列化和反序列化的问题
//要序列化的对象
public class Test {
private String id;
private String name;
public Test(String id,String name){
this.id = id;
this.name = name;
}
Test test = new Test("123","zhangshan");
序列化过程:
以key-value键值对为例:
redisTemplate.opsForValue.set("123",test)
//key的序列化
byte[] rawKey(Object key) {
Assert.notNull(key, "non null key required");
if (keySerializer() == null && key instanceof byte[]) {
return (byte[]) key;
}
return keySerializer().serialize(key);
}
//value的序列化
byte[] rawValue(Object value) {
if (valueSerializer() == null && value instanceof byte[]) {
return (byte[]) value;
}
return valueSerializer().serialize(value);
}
//RedisTemplate默认的序列化方式DefaultSerializer,把对象写入ObjectOutputStream
public void serialize(Object object, OutputStream outputStream) throws IOException {
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
}
//Jackson2JsonRedisSerializer的序列化方式
public byte[] serialize(Object source) throws SerializationException {
if (source == null) {
return EMPTY_ARRAY;
}
try {
return mapper.writeValueAsBytes(source);
} catch (JsonProcessingException e) {
throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
}
}
反序列化过程:
redisTemplate.opsForValue.get("123")
//value反序列化
V deserializeValue(byte[] value) {
if (valueSerializer() == null) {
return (V) value;
}
return (V) valueSerializer().deserialize(value);
}
//RedisTemplate默认的反序列化方式DefaultDeserializer,从ObjectInputStream中读取对象。
public Object deserialize(InputStream inputStream) throws IOException {
ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, this.classLoader);
try {
return objectInputStream.readObject();
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to deserialize object type", ex);
}
}
//Jackson2JsonRedisSerializer的反序列化方式
public <T> T deserialize(byte[] source, Class<T> type) throws SerializationException {
Assert.notNull(type,
"Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing.");
if (isEmpty(source)) {
return null;
}
try {
return mapper.readValue(source, type);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
跟加密和解密一样,序列化和反序列化时的方式也必须是对应的。用JdkSerializationRedisSerializer序列化后,再用Jackson2JsonRedisSerializer反序列化肯定会报错或取不到值。
二、RedisTemplate序列化需要注意的问题
1、很多微服务之间会用redis作分布式缓存。如果ServiceA用默认的序列化方式,而ServiceB用Jackson2JsonRedisSerializer或者其他的序列化方式,就会出现ServiceA把值存到redis缓存中,而ServiceB从redis缓存中取不到值的问题。最好的方式是各个微服务之间唯一确定好一种序列化方式,统一规范,统一配置。
2、万一出现了两个服务之间配置RedisTemplate的序列化方式和反序列化方式不一样导致从缓存中取不到值时,可以局部new一个新的RedisTemplate,设置相对应的反序列化方式,做下兼容处理。比如ServiceA的序列化方式为默认的,ServiceB的全局序列化方式为Jackson2JsonRedisSerializer,ServiceB可以在从缓存中取值时局部重新new一个RedisTemplate,设置序列化方式为默认的,然后用局部redisTemplate从缓存中取值。