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配置 }
问题原因分析
这个错误本质有两个核心点:
ResponseEntity是Spring提供的HTTP响应包装类,它没有无参构造函数,而Jackson默认依赖无参构造函数完成反序列化;- 从设计角度看,
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




