• Jackson 2.x 系列【29】Spring Boot 集成之 Redis 序列化/反序列化


    有道无术,术尚可求,有术无道,止于术。

    本系列Jackson 版本 2.17.0

    本系列Spring Boot 版本 3.2.4

    源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

    1. 前言

    Redis是一个常用的高性能非关系型内存数据库,接下来我们学习在Spring Boot中使用Redis时,集成基于Jackson序列化/反序列化

    2. RedisTemplate

    RedisTemplate是在Spring Boot环境中和Redis打交道的一个模板类,简化了与Redis数据库的交互过程,我们可以更加便捷地进行Redis的各种操作,如数据存取、异常处理及序列化等。

    StringRedisTemplateRedisTemplate的一个扩展,由于大多数针对Redis的操作都是基于字符串的,所以提供了一个专用的类来处理这些操作。

    Spring Boot自动配置中,已经帮我们注册了这个两个对象,使用时直接注入即可:

    @AutoConfiguration
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
    
    	@Bean
    	@ConditionalOnMissingBean(RedisConnectionDetails.class)
    	PropertiesRedisConnectionDetails redisConnectionDetails(RedisProperties properties) {
    		return new PropertiesRedisConnectionDetails(properties);
    	}
    
    	@Bean
    	@ConditionalOnMissingBean(name = "redisTemplate")
    	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    		RedisTemplate<Object, Object> template = new RedisTemplate<>();
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    
    	@Bean
    	@ConditionalOnMissingBean
    	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    		return new StringRedisTemplate(redisConnectionFactory);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3. RedisSerializer

    Spring Data声明了RedisSerializer接口,用于处理Redis序列化/反序列化,定义了相应的操作方法:

    public interface RedisSerializer<T> {
    
    	// 静态方法,直接返回不同类型的 RedisSerializer 实例
        static RedisSerializer<Object> java() {
            return java((ClassLoader)null);
        }
    
        static RedisSerializer<Object> java(@Nullable ClassLoader classLoader) {
            return new JdkSerializationRedisSerializer(classLoader);
        }
    
        static RedisSerializer<Object> json() {
            return new GenericJackson2JsonRedisSerializer();
        }
    
        static RedisSerializer<String> string() {
            return StringRedisSerializer.UTF_8;
        }
    
        static RedisSerializer<byte[]> byteArray() {
            return ByteArrayRedisSerializer.INSTANCE;
        }
    	
    	// 序列化
        @Nullable
        byte[] serialize(@Nullable T value) throws SerializationException;
    	
    	// 反序列化
        @Nullable
        T deserialize(@Nullable byte[] bytes) throws SerializationException;
    
        default boolean canSerialize(Class<?> type) {
            return ClassUtils.isAssignable(this.getTargetType(), type);
        }
    
        default Class<?> getTargetType() {
            return Object.class;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    默认提供了多种RedisSerializer实现:

    在这里插入图片描述

    简要说明

    • JdkSerializationRedisSerializer :默认配置,使用JDK自带的序列化机制将Java对象序列化为字节数组
    • OxmSerializer:用于序列化和反序列化XML数据
    • ByteArrayRedisSerializer:用于处理字节数组二进制数据,无需进行复杂的对象到字符串的转换,适用于大量二进制数据操作
    • StringRedisSerializer:将Java对象序列化为Redis可以存储的字符串形式
    • GenericToStringSerializer:通用的序列化器类,是将任意类型的数据对象转换为字符串形式,调用对象的toString()方法或自定义的序列化方法来获取字符串表示
    • GenericJackson2JsonRedisSerializer:将Java对象序列化为JSON格式的字符串形式,不需要设置类型信息,能够处理更多的动态类型,灵活性扩展性较低
    • Jackson2JsonRedisSerializer: 将Java对象序列化为JSON格式的字符串形式,必须提供要序列化对象的类型信息,每个类型都创建一个序列化器实例

    3.1 JdkSerializationRedisSerializer

    JdkSerializationRedisSerializer使用JDK自带的序列化机制,序列化Java对象为字节数组,反序列化字节数组为Java对象,是默认的选项。使用时,Java对象需要实现Serializable接口,存储的数据是不可读的。

    核心方法如下:

    	// 反序列化 
        public Object deserialize(@Nullable byte[] bytes) {
            if (SerializationUtils.isEmpty(bytes)) {
                return null;
            } else {
                try {
                    return this.deserializer.convert(bytes);
                } catch (Exception var3) {
                    throw new SerializationException("Cannot deserialize", var3);
                }
            }
        }
    	// 序列化
        public byte[] serialize(@Nullable Object object) {
            if (object == null) {
                return SerializationUtils.EMPTY_ARRAY;
            } else {
                try {
                    return (byte[])this.serializer.convert(object);
                } catch (Exception var3) {
                    throw new SerializationException("Cannot serialize", var3);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里使用默认提供的RedisTemplate进行存取操作:

        @Autowired
        RedisTemplate<Object,Object> redisTemplate;
    
        @Test
         void testRedisTemplate() {
            UserVO userVO = new UserVO();
            userVO.setId(1699657986705854464L);
            userVO.setUsername("jack");
            userVO.setBirthday(new Date());
            List<String> roleList = new ArrayList<>();
            roleList.add("管理员");
            roleList.add("经理");
            userVO.setRoleList(roleList);
            redisTemplate.opsForValue().set("userVO", userVO);
    
            // 查询
            UserVO o = (UserVO)redisTemplate.opsForValue().get("userVO");
            System.out.println(o);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Redis中可以看到存储的键值都是不可读的:

    在这里插入图片描述

    3.2 Jackson2JsonRedisSerializer

    Jackson2JsonRedisSerializer是基于Jackson实现的序列化器,序列化Java对象为JSON字符串,反序列化JSON字符串为Java对象。使用JSON字符串存储,结构清晰,容易阅读,存储的字节少,速度快,并且支持规则配置。

    可以看到内部维护了一个ObjectMapper

    public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {
    
        private ObjectMapper mapper;
        
        private final JacksonObjectReader reader;
        
        private final JacksonObjectWriter writer;
    	
    	// 序列化
        public T deserialize(@Nullable byte[] bytes) throws SerializationException {
            if (SerializationUtils.isEmpty(bytes)) {
                return null;
            } else {
                try {
                    return this.reader.read(this.mapper, bytes, this.javaType);
                } catch (Exception var3) {
                    throw new SerializationException("Could not read JSON: " + var3.getMessage(), var3);
                }
            }
        }
    	// 反序列化
        public byte[] serialize(@Nullable Object t) throws SerializationException {
            if (t == null) {
                return SerializationUtils.EMPTY_ARRAY;
            } else {
                try {
                    return this.writer.write(this.mapper, t);
                } catch (Exception var3) {
                    throw new SerializationException("Could not write JSON: " + var3.getMessage(), var3);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    4. 案例演示

    接下来我们演示如何配置Jackson2JsonRedisSerializer

    4.1 创建 RedisTemplate

    首先需要创建RedisTemplate并注册到容器中,指定泛型为,因为Redis是一个键值存储数据库,键直接使用字符串即可,而值一般都是多种类型的,统一用Object表示。

        @Bean("redisTemplate")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(factory); // 设置连接工厂
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2 创建 ObjectMapper

    创建ObjectMapper对象,可以根据需求进行相应的配置:

            // 创建 ObjectMapper
            ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
                    .json()
                    .serializationInclusion(JsonInclude.Include.NON_NULL) // 不为 null 才序列化
                    .visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) // 可见性,只序列化任意修饰符的字段
                    .indentOutput(true) // 美化格式
                    .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // 关闭某个特征
                    .build();
            // 启用自动包含类型信息,用于反序列化时重建对象的实际类型
            objectMapper.activateDefaultTyping(
                    objectMapper.getPolymorphicTypeValidator(),  //  验证器,用于验证实际要反序列化的子类型是否有效
                    ObjectMapper.DefaultTyping.NON_FINAL, // 定义哪些类型的对象需要添加额外的类型信息,NON_FINAL:非 final类都会包含
                    JsonTypeInfo.As.PROPERTY); // 类型信息的包含方式 PROPERTY:类型信息作为JSON对象的一个属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.3 创建 Jackson2JsonRedisSerializer

    创建Jackson2JsonRedisSerializer,并设置创建好的ObjectMapper对象:

            // 创建 Jackson2JsonRedisSerializer
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
    
    • 1
    • 2

    4.4 设置序列化器

    设置Key的序列化器为StringRedisSerializer,设置值的序列化器为Jackson2JsonRedisSerializer

            // 设置键值的序列化器
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    
            // 设置Hash 键值的序列化器
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    最终代码如下所示:

    @Configuration
    public class RedisConfig {
    
        @Bean("redisTemplate")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            // 创建 RedisTemplate
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(factory); // 设置连接工厂
    
            // 创建 ObjectMapper
            ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
                    .json()
                    .serializationInclusion(JsonInclude.Include.NON_NULL) // 不为 null 才序列化
                    .visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) // 可见性,只序列化任意修饰符的字段
                    .indentOutput(true) // 美化格式
                    .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // 关闭某个特征
                    .build();
            // 启用自动包含类型信息,用于反序列化时重建对象的实际类型
            objectMapper.activateDefaultTyping(
                    objectMapper.getPolymorphicTypeValidator(),  //  验证器,用于验证实际要反序列化的子类型是否有效
                    ObjectMapper.DefaultTyping.NON_FINAL, // 定义哪些类型的对象需要添加额外的类型信息,NON_FINAL:非 final类都会包含
                    JsonTypeInfo.As.PROPERTY); // 类型信息的包含方式 PROPERTY:类型信息作为JSON对象的一个属性
    
            // 创建 Jackson2JsonRedisSerializer
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
    
            // 设置键值的序列化器
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    
            // 设置Hash 键值的序列化器
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            return redisTemplate;
        }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    4.5 测试

    注入RedisTemplate并执行存取操作:

        @Autowired
        RedisTemplate<String,Object> redisTemplate;
    
    • 1
    • 2

    查看Redis
    在这里插入图片描述

  • 相关阅读:
    python基于PHP+MySQL的英语在线测评学习网站
    Python学习笔记--生成器
    R语言dplyr包distinct函数基于dataframe数据中的所有变量移除重复行
    MasaFramework的MinimalAPI设计
    Java学习到面试所遇的小小问题
    一起来打靶 02
    谷歌创新框架:从非结构化数据,实现多模态学习
    房产交易软件开发
    基于nodejs的学院会议纪要管理系统-计算机毕业设计
    Java:如何在PowerPoint幻灯片中创建散点图
  • 原文地址:https://blog.csdn.net/qq_43437874/article/details/137730379