You need to enable JavaScript to run this app. RedisTemplate反序列化含多态对象属性的实例时抛出异常
优惠活动
大模型
产品
解决方案
定价
更多

RedisTemplate反序列化含多态对象属性的实例时抛出异常

问题根因

当前配置的Jackson序列化器没有开启多态对象的类型元数据写入能力:

  • 序列化阶段,Jackson仅按照字段的静态声明类型(也就是Father)生成JSON,不会把实际存入的Son子类的类标识、独有属性映射规则写入结果
  • 反序列化阶段,Jackson拿到带sonName属性的JSON片段,严格按照TargetObj中声明的Father类型解析,遇到未在Father类中定义的sonName字段,就会直接抛出无法识别的异常
解决方案

1. 调整RedisConfig中的ObjectMapper配置

在原有配置基础上,开启带安全校验的多态类型支持,让Jackson序列化时自动把对象的实际类名作为元数据写入JSON,反序列化时根据元数据还原成正确的子类实例。
针对Spring Boot 2.4.3内置的Jackson 2.11.x版本,使用非过时的API配置,避免旧版本enableDefaultTyping带来的安全漏洞:

  • 首先配置类型校验规则,仅放行自己项目包下的类做反序列化,避免外部恶意类的反序列化攻击
  • 激活多态默认类型规则,所有非final的对象类型序列化时自动携带@class类型标记

调整后的完整RedisConfig代码如下:

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        // 新增带安全校验的多态配置
        BasicPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfBaseType("com.hyc.backend.") // 替换为自己项目的实际根包路径
                .build();
        om.activateDefaultTyping(
                ptv,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );

        jackson2JsonRedisSerializer.setObjectMapper(om);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

配置完成后,序列化Son实例生成的JSON片段会类似:

{"@class":"com.hyc.backend.dao.Son","fatherName":"fatherName","sonName":"sonName"}

反序列化时Jackson看到@class标记,就会直接还原成对应的Son实例,不会再硬按Father类型解析。

2. 修正测试代码的使用方式

原有测试代码里自己new了一个全新的ObjectMapper做convertValue转换,这个新的ObjectMapper没有配置上述多态规则,就算Redis里存储的JSON带了完整的类型元数据,也会触发同样的报错。

正确的测试逻辑应该直接使用RedisTemplate的内置序列化能力取出反序列化完成的对象,不需要自己手动新建ObjectMapper转换:

TargetObj to = new TargetObj();
to.setId(1);
to.setName("toname");
Son son = new Son();
son.setFatherName("fatherName");
son.setSonName("sonName");
to.setProperty(son);
redisTemplate.opsForValue().set("test", to);
// 直接通过RedisTemplate取出已经反序列化完成的对象
TargetObj test = (TargetObj) redisTemplate.opsForValue().get("test");
// 此时property字段实际为Son类型,可正常获取所有子类属性
Son actualProperty = (Son) test.getProperty();
System.out.println(actualProperty.getSonName()); // 正常输出"sonName"

可选替代方案

如果不想全局开启多态序列化带来的JSON体积增大问题,可以针对需要支持多态的字段单独加Jackson注解,只让对应字段保留类型标记:
在TargetObj的property字段上添加注解:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
private Father property;

这种方式仅对单个字段生效,不需要调整全局ObjectMapper配置,适合多态场景比较少的项目。

注意事项
  • 多态配置必须加安全校验规则,禁止使用无校验的全类放行配置,否则会存在远程反序列化的安全漏洞
  • 如果类被final修饰,NON_FINAL的默认规则不会为其添加类型标记,这类类不需要多态支持的话可以正常使用
  • 调整配置后,需要清空之前存在Redis里的旧测试数据,旧数据没有携带类型标记,还是会触发反序列化报错

内容的提问来源于stack exchange,提问作者Kolin Huang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 11:36:03