You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的序列化器,步骤如下:

  1. 添加RedisCacheConfiguration配置,指定key和value的序列化器与RedisTemplate一致
  2. 基于该配置创建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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:33:12