You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Spring Boot序列化ResponseEntity到Redis时反序列化失败求助

问题描述

当我尝试将ResponseEntity序列化到Redis时,遇到了如下Jackson反序列化错误:

嵌套异常为com.fasterxml.jackson.databind.JsonMappingException:无法构造org.springframework.http.ResponseEntity实例:找不到合适的构造函数,无法从对象值反序列化(缺少默认构造函数或创建者,或者可能需要添加/启用类型信息?)

我的控制器代码:

@Cacheable(value = "restapi", keyGenerator = "customKeyGenerator")
@RequestMapping(value = "/restapi/{id}", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" })
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Object> findRestApi(@PathVariable("id") Integer id) {
    RestModel model= new RestModel ();
    model = restService.findRestById(id);
    return new ResponseEntity<>(model, headers, HttpStatus.OK);
}

Redis配置类核心代码:

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
    // ... 其他配置
    @Bean
    RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory rcf) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(rcf);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new JsonRedisSerializer());
        return template;
    }

    static class JsonRedisSerializer implements RedisSerializer<Object> {
        private final ObjectMapper om;

        public JsonRedisSerializer() {
            this.om = new ObjectMapper().enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
        }

        @Override
        public byte[] serialize(Object t) throws SerializationException {
            try {
                return om.writeValueAsBytes(t);
            } catch (JsonProcessingException e) {
                throw new SerializationException(e.getMessage(), e);
            }
        }

        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            if(bytes == null){
                return null;
            }
            try {
                return om.readValue(bytes, Object.class);
            } catch (Exception e) {
                throw new SerializationException(e.getMessage(), e);
            }
        }
    }
    // ... 其他Bean配置
}

问题原因分析

这个错误本质有两个核心点:

  1. ResponseEntity是Spring提供的HTTP响应包装类,它没有无参构造函数,而Jackson默认依赖无参构造函数完成反序列化;
  2. 从设计角度看,ResponseEntity包含HTTP状态码、响应头等元数据,这些内容并不适合被缓存——缓存的核心应该是业务数据,而非HTTP响应的包装结构。

解决方案

方案1:缓存业务对象(推荐)

这是最合理的做法,我们只缓存实际的业务数据RestModel,而非整个ResponseEntity

修改控制器代码:移除控制器方法上的@Cacheable注解,把缓存逻辑移到业务服务层:

@RequestMapping(value = "/restapi/{id}", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" })
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Object> findRestApi(@PathVariable("id") Integer id) {
    RestModel model = restService.findRestById(id);
    return new ResponseEntity<>(model, headers, HttpStatus.OK);
}

修改服务层代码,给业务查询方法添加缓存注解:

@Service
public class RestService {
    @Cacheable(value = "restapi", keyGenerator = "customKeyGenerator")
    public RestModel findRestById(Integer id) {
        // 这里是你的业务逻辑,比如从数据库查询数据
        return new RestModel();
    }
}

这种方式既解决了序列化问题,也符合缓存的设计原则——缓存有复用价值的业务数据,而非HTTP响应包装。

方案2:配置Jackson支持ResponseEntity反序列化(不推荐)

如果因为特殊场景必须缓存ResponseEntity,可以通过配置Jackson来兼容它的序列化/反序列化:

在你的JsonRedisSerializer中,给ObjectMapper添加Spring官方的Jackson模块,它会自动处理ResponseEntity这类Spring类的序列化逻辑:

static class JsonRedisSerializer implements RedisSerializer<Object> {
    private final ObjectMapper om;

    public JsonRedisSerializer() {
        this.om = new ObjectMapper()
                .enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY)
                // 注册Spring Jackson模块,处理Spring专属类的序列化
                .registerModule(new SpringModule());
    }

    // ... 原有序列化/反序列化方法
}

总结

优先选择方案1,这是从根源上解决问题的最优解。如果确实有特殊需求要缓存ResponseEntity,再考虑方案2。

内容的提问来源于stack exchange,提问作者Luis Gustavo Souza

火山引擎 最新活动