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. 前言](#1. 前言)
    • [2. RedisTemplate](#2. RedisTemplate)
    • [3. RedisSerializer](#3. RedisSerializer)
      • [3.1 JdkSerializationRedisSerializer](#3.1 JdkSerializationRedisSerializer)
      • [3.2 Jackson2JsonRedisSerializer](#3.2 Jackson2JsonRedisSerializer)
    • [4. 案例演示](#4. 案例演示)
      • [4.1 创建 RedisTemplate](#4.1 创建 RedisTemplate)
      • [4.2 创建 ObjectMapper](#4.2 创建 ObjectMapper)
      • [4.3 创建 Jackson2JsonRedisSerializer](#4.3 创建 Jackson2JsonRedisSerializer)
      • [4.4 设置序列化器](#4.4 设置序列化器)
      • [4.5 测试](#4.5 测试)

1. 前言

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

2. RedisTemplate

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

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

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

java 复制代码
@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);
	}
}

3. RedisSerializer

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

java 复制代码
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;
    }
}

默认提供了多种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接口,存储的数据是不可读的。

核心方法如下:

java 复制代码
	// 反序列化 
    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);
            }
        }
    }

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

java 复制代码
    @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);
    }

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

3.2 Jackson2JsonRedisSerializer

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

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

java 复制代码
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);
            }
        }
    }
}

4. 案例演示

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

4.1 创建 RedisTemplate

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

java 复制代码
    @Bean("redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory); // 设置连接工厂
}

4.2 创建 ObjectMapper

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

java 复制代码
        // 创建 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对象的一个属性

4.3 创建 Jackson2JsonRedisSerializer

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

java 复制代码
        // 创建 Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);

4.4 设置序列化器

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

java 复制代码
        // 设置键值的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置Hash 键值的序列化器
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

最终代码如下所示:

java 复制代码
@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;
    }
    }
}

4.5 测试

注入RedisTemplate并执行存取操作:

java 复制代码
    @Autowired
    RedisTemplate<String,Object> redisTemplate;

查看Redis

相关推荐
岁月变迁呀4 小时前
Redis梳理
数据库·redis·缓存
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
Code apprenticeship6 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站6 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶6 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
AskHarries7 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion8 小时前
Springboot的创建方式
java·spring boot·后端
Yvemil78 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
星河梦瑾9 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富9 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua