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

相关推荐
Flittly1 小时前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
人活一口气6 小时前
从JVM调优到MCP协议:Java全栈技术体系深度总结与企业级架构实践
java·spring boot
Java陈序员1 天前
企业级!一个基于 Java 开发的开源 AI 应用开发平台!
spring boot·agent·mcp
杨运交1 天前
[041][公共模块]分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架
spring boot
用户3074596982072 天前
Redis 延时队列详解
redis
烤代码的吐司君2 天前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
Flittly2 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
Flynt3 天前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
掉鱼的猫4 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot
leeyi4 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent