Spring-data-redis @Cacheable出现LinkedHashMap转MyObject类型转换异常
嘿,这个问题太常见了!我之前在做Spring Boot缓存的时候也踩过这个坑,本质就是Redis反序列化时丢失了对象的类型信息导致的。
咱们先理清楚为什么第一次调用没问题,第二次就炸了:
- 第一次调用API时,直接从Mongo获取
MyObject实例,返回给客户端的过程中,对象的类型是明确的,所以一切正常; - 第二次调用时,Redis返回的是序列化后的JSON字符串,但默认的序列化器(比如
Jackson2JsonRedisSerializer)在序列化时没有把MyObject的全类名存进去,反序列化的时候只能把JSON转成通用的LinkedHashMap,这时候你强转成MyObject当然会抛出ClassCastException。
解决方案1:使用带类型信息的序列化器
最直接的办法是用GenericJackson2JsonRedisSerializer代替默认的序列化器,它会在序列化后的JSON中自动加入@class字段,记录对象的全类名,反序列化时就能精准识别类型了。
你可以通过配置RedisTemplate和CacheManager来实现:
@Configuration public class RedisCacheConfig { // 配置RedisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // 初始化GenericJackson2JsonRedisSerializer,自带类型信息存储 GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); // 设置value和hashValue的序列化器 redisTemplate.setValueSerializer(jsonSerializer); redisTemplate.setHashValueSerializer(jsonSerializer); // key用String序列化器就行 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } // 如果用@Cacheable这类Spring Cache注解,必须配置CacheManager @Bean public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(cacheConfig) .build(); } }
解决方案2:自定义ObjectMapper并开启类型映射
如果你需要更精细地控制Jackson的序列化行为,可以手动配置ObjectMapper,开启默认类型映射,然后传给GenericJackson2JsonRedisSerializer:
@Bean public GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer() { ObjectMapper objectMapper = new ObjectMapper(); // 开启类型信息,NON_FINAL表示除了final类都记录类型 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); return new GenericJackson2JsonRedisSerializer(objectMapper); }
重要提醒!
配置完新的序列化器后,一定要清空Redis中已有的缓存数据!因为之前缓存的JSON是没有类型信息的,即使换了序列化器,读取旧数据还是会报错。
这样调整之后,第二次从Redis读取时,就能正确把JSON反序列化为MyObject实例,不会再出现ClassCastException了。
内容的提问来源于stack exchange,提问作者hardik.desai
相关产品推荐
相关产品推荐

