背景
在对 BSON 的 ObjectId 类型与 Java 的 LocalDate 类型进行序列化与反序列化时,Redis 出现了无法对 ObjectId 、LocalDate 进行序列化的报错。
配置 ReactiveRedisTemplate<String, Object>
于是作者决定在 Redis 的配置类中对 ReactiveRedisTemplate<String, Object> 进行定制,在阅读源码后发现 ReactiveRedisTemplate 的构造函数中可以传入一个 RedisSerializationContext<String, Object> 对象,对 Redis 序列化方式进行定制:
@Bean
public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(
ReactiveRedisConnectionFactory reactiveRedisConnectionFactory,
RedisSerializationContext<String, Object> redisSerializationContext) {
return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, redisSerializationContext);
}
配置 RedisSerializationContext<String, Object>
查看 RedisSerializationContext<K, V> 接口对源码后,发现其内部有一个静态接口 RedisSerializationContextBuilder<K, V> ,专门用于构建 RedisSerializationContext<K, V> 对象,其 key() 、value() 、hashKey() 、hashValue() 等方法支持对序列化方式进行定制化:
interface RedisSerializationContextBuilder<K, V> { ... }
于是对 RedisSerializationContext<String, Object> 进行定制,使用自定义的序列化方式对 Redis 的 value 与 hashValue 进行特制的序列化:
@Bean
public RedisSerializationContext<String, Object> redisSerializationContext() {
RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder = RedisSerializationContext.newSerializationContext();
builder.key(StringRedisSerializer.UTF_8);
builder.value(serializer());
builder.hashKey(StringRedisSerializer.UTF_8);
builder.hashValue(serializer());
return builder.build();
}
发现 RedisSerializationContext.RedisSerializationContextBuilder<String, Object> 接口的 value(...) 与 hashValue(...) 方法需要传入一个 RedisSerializer 对象,根据之前的项目经验,决定对其实现类 Jackson2JsonRedisSerializer 进行定制。
定制 Jackson2JsonRedisSerializer 的 ObjectMapper
在 Jackson2JsonRedisSerializer 类中,发现有一个 ObjectMapper 类型的字段,根据其在 deserialize 方法与 serialize 方法中被调用的方式,不难猜出其负责对象的映射,故选择对 ObjectMapper 也进行定制化:
@SuppressWarnings("unchecked")
public T deserialize(@Nullable byte[] bytes) throws SerializationException {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return (T) this.objectMapper.readValue(bytes, 0, bytes.length, javaType);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
@Override
public byte[] serialize(@Nullable Object t) throws SerializationException {
if (t == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return this.objectMapper.writeValueAsBytes(t);
} catch (Exception ex) {
throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
}
}
在 ObjectMapper 中发现了一个 registerModule 方法,支持定制序列化器与反序列化器:
public ObjectMapper registerModule(Module module) { ... }
将定制的 Module 注册进 ObjectMapper
使用 JavaTimeModule 序列化 LocalDate 类
Module 是一个 abstruct class ,在其子类中,找到了 JavaTimeModule 类,其支持对 LocalDate 等时间类进行序列化,故将 JavaTimeModule 对象注册进 ObjectMapper 对象:
objectMapper.registerModule(new JavaTimeModule());
定制 SimpleModule 序列化 ObjectId 类
但是未找到对 BSON 的 ObjectId 类型进行序列化的 Module ,于是构造一个 SimpleModule 对象,调用 addSerializer(...) 与 addDeserializer(...) 方法进行序列化与反序列化的定制(在此使用 ObjectId 的十六进制形式的字符串进行序列化),并将其注册进 ObjectMapper 对象:
SimpleModule objectIdModule = new SimpleModule("ObjectIdModule");
objectIdModule.addSerializer(ObjectId.class, new JsonSerializer<ObjectId>() {
@Override
public void serialize(ObjectId objectId, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(objectId.toString());
}
});
objectIdModule.addDeserializer(ObjectId.class, new JsonDeserializer<ObjectId>() {
@Override
public ObjectId deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
return new ObjectId(jsonParser.readValueAs(String.class));
}
});
objectMapper.registerModule(objectIdModule);
至此,完成 Redis 对 ObjectId 与 LocalDate 类进行定制的工作。
Redis 配置文件
package cn.tzq0301.auth.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.bson.types.ObjectId;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.IOException;
@SpringBootConfiguration
public class RedisConfig {
public static final String PROJECT_NAMESPACE_PREFIX = "pcs:";
public static final String USER_NAMESPACE_PREFIX = PROJECT_NAMESPACE_PREFIX + "user:";
@Bean
public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(
ReactiveRedisConnectionFactory reactiveRedisConnectionFactory,
RedisSerializationContext<String, Object> redisSerializationContext) {
return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, redisSerializationContext);
}
@Bean
public RedisSerializationContext<String, Object> redisSerializationContext() {
RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder = RedisSerializationContext.newSerializationContext();
builder.key(StringRedisSerializer.UTF_8);
builder.value(serializer());
builder.hashKey(StringRedisSerializer.UTF_8);
builder.hashValue(serializer());
return builder.build();
}
private Jackson2JsonRedisSerializer<Object> serializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule objectIdModule = new SimpleModule("ObjectIdModule");
objectIdModule.addSerializer(ObjectId.class, new JsonSerializer<ObjectId>() {
@Override
public void serialize(ObjectId objectId, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(objectId.toString());
}
});
objectIdModule.addDeserializer(ObjectId.class, new JsonDeserializer<ObjectId>() {
@Override
public ObjectId deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
return new ObjectId(jsonParser.readValueAs(String.class));
}
});
objectMapper.registerModule(objectIdModule);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
}
|