我們先來看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); }
主要添加了加紅的代碼,就能順利的反序列化出目標類型。