我们先来看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);
}
主要添加了加红的代码,就能顺利的反序列化出目标类型。