IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> WebFlux 响应式定制 Redis 对指定数据类型(例如 ObjectId、LocalDate)的序列化与反序列化配置 -> 正文阅读

[大数据]WebFlux 响应式定制 Redis 对指定数据类型(例如 ObjectId、LocalDate)的序列化与反序列化配置

背景

在对 BSON 的 ObjectId 类型与 Java 的 LocalDate 类型进行序列化与反序列化时,Redis 出现了无法对 ObjectIdLocalDate 进行序列化的报错

配置 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 进行定制

定制 Jackson2JsonRedisSerializerObjectMapper

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 对 ObjectIdLocalDate 类进行定制的工作。

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;
    }
}
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:36:19  更:2022-03-12 17:36:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:45:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码