irpas技术客

WebFlux 响应式定制 Redis 对指定数据类型(例如 ObjectId、LocalDate)的序列化与反序列化配置_油条生煎_webflux 序列化

网络 7212

背景

在对 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) { // 配置 RedisSerializationContext return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, redisSerializationContext); } 配置 RedisSerializationContext<String, Object>

查看 RedisSerializationContext<K, V> 接口对源码后,发现其内部有一个静态接口 RedisSerializationContextBuilder<K, V>,专门用于构建 RedisSerializationContext<K, V> 对象,其 key()、value()、hashKey()、hashValue() 等方法支持对序列化方式进行定制化:

/** * Builder for {@link RedisSerializationContext}. * * @author Mark Paluch * @author Christoph Strobl */ 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 方法,支持定制序列化器与反序列化器:

/** * Method for registering a module that can extend functionality * provided by this mapper; for example, by adding providers for * custom serializers and deserializers. * * @param module Module to register */ 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; /** * @author tzq0301 * @version 1.0 */ @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(); // ObjectId 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); // LocalDate objectMapper.registerModule(new JavaTimeModule()); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); return jackson2JsonRedisSerializer; } }


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Webflux #序列化 #背景在对 #BSON # #ObjectId #类型与 #JAVA