我们先来看StringRedisTemplate一个简单操作Redis的使用过程:
StringRedisTemplate redisTemplate = new StringRedisTemplate(); //操作String redisTemplate.boundValueOps("test").set("val"); User user = new User(); user.setName("aaa"); user.setAge(100); //操作Hash redisTemplate.boundHashOps("test").put("key1", user);
真正执行redis的set方法如下:
@Override public void set(K key, V value) { byte[] rawValue = rawValue(value); execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }, true); }
rawValue调用的是StringRedisTemplate的序列化方法valueSerializer().serialize(value)
ValueDeserializingRedisCallback内部调用的是valueSerializer().deserialize(value),
所以主要核心都是在RedisTemplate的序列化器中实现。
StringRedisTemplate继承自RedisTemplate<String,String>
public StringRedisTemplate() { setKeySerializer(RedisSerializer.string()); setValueSerializer(RedisSerializer.string()); setHashKeySerializer(RedisSerializer.string()); setHashValueSerializer(RedisSerializer.string()); }
从构造函数中可以看出设置了StringRedisSerializer序列化器,StringRedisSerializer的实现如下:
public class StringRedisSerializer implements RedisSerializer<String> { private final Charset charset; public static final StringRedisSerializer UTF_8 = new StringRedisSerializer(StandardCharsets.UTF_8); public StringRedisSerializer(Charset charset) { Assert.notNull(charset, "Charset must not be null!"); this.charset = charset; } @Override public String deserialize(@Nullable byte[] bytes) { return (bytes == null ? null : new String(bytes, charset)); } @Override public byte[] serialize(@Nullable String string) { return (string == null ? null : string.getBytes(charset)); } }
我们可以看到实现序列化serialize和反序列化deserialize只是将字符串转成字节数组,字节数组转字符串。
然后我们看RedisTemplate<Object,Object>,继承自RedisAccessor,RedisAccessor又继承了InitializingBean接口,我们可以看到afterPropertiesSet方法:
@Override 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<>(this); } initialized = true; }
上面的主要是如果没设置序列化方式,就使用默认的JdkSerializationRedisSerializer,接下来我们看默认的序列化:
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> { private final Converter<Object, byte[]> serializer; private final Converter<byte[], Object> deserializer; public JdkSerializationRedisSerializer() { this(new SerializingConverter(), new DeserializingConverter()); } public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) { this(new SerializingConverter(), new DeserializingConverter(classLoader)); } public JdkSerializationRedisSerializer(Converter<Object, byte[]> serializer, Converter<byte[], Object> deserializer) { Assert.notNull(serializer, "Serializer must not be null!"); Assert.notNull(deserializer, "Deserializer must not be null!"); this.serializer = serializer; this.deserializer = deserializer; } public Object deserialize(@Nullable byte[] bytes) { if (SerializationUtils.isEmpty(bytes)) { return null; } try { return deserializer.convert(bytes); } catch (Exception ex) { throw new SerializationException("Cannot deserialize", ex); } } @Override public byte[] serialize(@Nullable Object object) { if (object == null) { return SerializationUtils.EMPTY_ARRAY; } try { return serializer.convert(object); } catch (Exception ex) { throw new SerializationException("Cannot serialize", ex); } } }
从上我们可以看到主要使用的是SerializingConverter和DeserializingConverter对象,接下来看SerializingConverter:
public class SerializingConverter implements Converter<Object, byte[]> { private final Serializer<Object> serializer; public SerializingConverter() { this.serializer = new DefaultSerializer(); } @Override public byte[] convert(Object source) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); try { this.serializer.serialize(source, byteStream); return byteStream.toByteArray(); } catch (Throwable ex) { throw new SerializationFailedException("Failed to serialize object using " + this.serializer.getClass().getSimpleName(), ex); } } }
public class DefaultSerializer implements Serializer<Object> { @Override 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(); } }
通过如上可以看到主要使用的是jdk本省的对象序列化。
工作中我们一般喜欢使用json格式存储在redis中,spring redis也提供了Jackson2JsonRedisSerializer序列化器,使用方式如下:
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); RedisTemplate<String, User> redisTemplate = new RedisTemplate<>(); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //redisConnectionFactory这里是从容器注入的 redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.afterPropertiesSet(); User user = new User(); user.setName("aaa"); user.setAge(100); redisTemplate.boundValueOps("test").set(user); Object res = redisTemplate.boundValueOps("test").get(); System.out.println("res:" + res);
但是最后我们发现res的类型并不是User类型,而是class java.util.LinkedHashMap类型。但这不是我们要的结果,下面直接使用Jackson2JsonRedisSerializer来分析下:
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); // om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); User user = new User(); user.setName("aaa"); user.setAge(100); byte[] bt = jackson2JsonRedisSerializer.serialize(user); Object obj = jackson2JsonRedisSerializer.deserialize(bt); User user1 = (User) obj; System.out.println(user1);
通过调试我们拿到bt序列化的数据看下图:
最后反序列化的结果就是 java.util.LinkedHashMap类型。
那怎样才能反序列出我们想要的User类型了,我们把上面注释的两行代码放开,再次测试如下:
最后能成功反序列出User类型,我们发现序列化的结果里面含有的序列化的类型信息,所以这也是能通过反射方式能得到我们想要的类型的原理。但他的优势就比JdkSerializationRedisSerializer轻量很多,我们看JdkSerializationRedisSerializer序列化的结果为:
从结果我们可以看出对象序列化包含了类信息和属性信息,这样数据会冗余很多。
最后我们把前面通过Jackson2JsonRedisSerializer序列化redis的代码重新更改如下:
@Test public void testRedis2() { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisTemplate<String, User> redisTemplate = new RedisTemplate<>(); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.afterPropertiesSet(); User user = new User(); user.setName("aaa"); user.setAge(100); redisTemplate.boundValueOps("test").set(user); Object res = redisTemplate.boundValueOps("test").get(); System.out.println("res:" + res); }
主要添加了加红的代码,就能顺利的反序列化出目标类型。