springboot的RedisTemplate序列化问题

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 deserialize(byte[] source, Class 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从缓存中取值。

你可能感兴趣的:(java)