Spring声明式注解缓存与RedisTemplate兼容性问题求助
问题描述
维护遗留项目时,混用Spring声明式缓存注解与RedisTemplate操作Redis出现序列化异常:
- 使用
@CachePut存入数据,通过RedisTemplate.opsForValue().get()读取时抛出JsonParseException - 使用
RedisTemplate.opsForValue().set()存入数据,通过@Cacheable读取时抛出SerializationException
相关代码实现:
Spring注解缓存代码
@CachePut(value = "API", key = "#key") public Object upsert(String key, Object value) { return value; } @Cacheable(value = "API", key = "#key") public Object get(String key) { return null; }
RedisTemplate操作代码
public Object getValue(String key) { ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); return valueOperations.get(key); } public void setValue(String key, Object value) { ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); valueOperations.set(key, value); }
批量查询代码
protected List<Object> multiGet(List<String> keys) { List<Object> cachedDataList = new ArrayList<>(); try { List<Object> cacheObjects = redisTemplate.opsForValue().multiGet(keys); if (cacheObjects != null) { cachedDataList.addAll(cacheObjects); } else { log.debug("getStateCacheDataBatch: null values for {}", keys); } } catch (Exception e) { log.error("Error while fetching batch state cached data for keys {}", keys, e); } return cachedDataList; }
当前RedisTemplate配置
@Bean RedisTemplate<String, Object> redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory) { final RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; }
原因分析
你以为两者序列化配置相同,但实际上Spring Cache默认的RedisCacheManager并未使用你自定义的RedisTemplate的序列化器。Spring Cache的RedisCache默认采用JdkSerializationRedisSerializer序列化value,而你自定义的RedisTemplate用的是GenericJackson2JsonRedisSerializer,两者序列化格式完全不同,导致跨方式读写时解析失败。
解决方案
统一Spring Cache和RedisTemplate的序列化规则,通过配置RedisCacheManager使其复用RedisTemplate的序列化器,步骤如下:
- 添加RedisCacheConfiguration配置,指定key和value的序列化器与RedisTemplate一致
- 基于该配置创建RedisCacheManager,并替换默认的缓存管理器
具体配置代码:
@Bean RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory, RedisTemplate<String, Object> redisTemplate) { RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig() // 缓存key序列化器和RedisTemplate保持一致 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer())) // 缓存value序列化器和RedisTemplate保持一致 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())) // 可按需添加过期时间等额外配置 .entryTtl(Duration.ofHours(1)); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(cacheConfig) // 针对特定缓存空间(比如"API")复用配置,可删除或按需修改 .withCacheConfiguration("API", cacheConfig) .build(); }
验证要点
- 清理Redis中原有缓存数据(避免新旧格式冲突)
- 重新测试两种写入方式的交叉读取:
@CachePut存入后用RedisTemplate.get()读取RedisTemplate.set()存入后用@Cacheable读取
- 批量查询
multiGet方法需能正常解析注解存入的数据
内容的提问来源于stack exchange,提问作者Happs
相关产品推荐
相关产品推荐

