HCRM博客

为什么出现cacheable value报错?

在软件开发中,缓存是一种常用的优化技术,用于提高系统性能,在使用缓存时,可能会遇到各种问题,“cacheable value报错” 是比较常见的一种,下面将详细解析这类错误的常见原因、解决方法,并提供相关示例和FAQs。

一、常见原因及解决方法

1、缓存中不允许存储null值

为什么出现cacheable value报错?-图1
(图片来源网络,侵权删除)

错误信息Cache 'cache:getCustRange' does not allow 'null' values. Avoid storing null via '@Cacheable(unLess="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration

原因:当使用@Cacheable 注解的方法返回值为 null 时,如果缓存配置不允许存储 null 值,就会抛出此异常。

解决方法

方法一:在@Cacheable 注解中添加unless="#result == null" 条件,这样当结果为 null 时,就不会将其存入缓存。

方法二:如果是使用 Redis 作为缓存,可以在RedisCacheConfiguration 中配置允许缓存 null 值。

2、序列化错误

为什么出现cacheable value报错?-图2
(图片来源网络,侵权删除)

错误信息:例如使用 Jackson 序列化时,可能出现com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token 等类似错误。

原因:通常是因为缓存的对象中包含的字段类型与序列化方式不匹配,或者缓存的 key 或 value 在序列化过程中出现了问题,对象的某个字段是 List 类型,但接收到的数据格式不正确;或者在使用 Redis 缓存时,没有正确配置序列化器等。

解决方法

检查对象的字段类型是否正确,确保序列化和反序列化过程中数据类型一致。

对于 Redis 缓存,正确配置序列化器和反序列化器,可以使用 Jackson2JsonRedisSerializer 等序列化器,并在配置中指定。

3、缓存配置错误

为什么出现cacheable value报错?-图3
(图片来源网络,侵权删除)

错误信息:如使用 Caffeine 缓存时,可能出现缓存未生效的情况。

原因:可能是缓存配置不正确,例如缓存的过期时间设置不合理,或者缓存的 key 生成策略有问题等,如果在同一个类中,一个方法调用另一个有缓存注解的方法,由于是基于动态代理实现的缓存,内部调用不会走代理,所以缓存也不会生效。

解决方法

仔细检查缓存配置,包括过期时间、最大容量等参数是否设置合理。

对于缓存 key 的生成,要确保其唯一性和正确性,如果是类内部方法调用导致缓存不生效,可以将方法拆分到不同类中,或者通过其他方式手动控制缓存逻辑。

4、包导入错误

错误信息:如使用@Cacheable 注解时,提示找不到key 参数等。

原因:可能是因为导入了错误的包,导致注解无法正确识别和使用。

解决方法:确保导入的是org.springframework.cache.annotation.Cacheable 包下的注解。

二、示例代码

以解决缓存中不允许存储 null 值的问题为例:

  • import org.springframework.cache.annotation.Cacheable;
  • import org.springframework.context.annotation.Bean;
  • import org.springframework.context.annotation.Configuration;
  • import org.springframework.data.redis.cache.RedisCacheConfiguration;
  • import org.springframework.data.redis.serializer.RedisSerializationContext;
  • import org.springframework.data.redis.serializer.RedisSerializer;
  • import org.springframework.data.redis.serializer.StringRedisSerializer;
  • import com.fasterxml.jackson.databind.ObjectMapper;
  • import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
  • @Configuration
  • public class CacheConfig {
  • @Bean
  • public RedisCacheConfiguration redisCacheConfiguration() {
  • RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
  • // 设置允许缓存 null 值
  • config = config.disableCachingNullValues();
  • // 自定义序列化方式
  • ObjectMapper objectMapper = new ObjectMapper();
  • objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
  • RedisSerializer<Object> jsonSerializer = RedisSerializer.json(objectMapper);
  • config = config.serializeKeysWith(StringRedisSerializer.UTF_8)
  • .serializeValuesWith(jsonSerializer);
  • return config;
  • }
  • }
  • // 服务类中使用 @Cacheable 注解
  • @Service
  • public class MyService {
  • @Cacheable(value = "myCache", key = "#id", unless = "#result == null")
  • public MyObject getById(String id) {
  • // 查询数据库或其他数据源获取数据
  • return myRepository.findById(id);
  • }
  • }

三、相关问答FAQs

1、为什么在方法内部调用另一个有缓存注解的方法时,缓存不生效?

这是因为基于 Spring AOP 代理实现的缓存,内部方法调用不会走代理,所以缓存注解不会生效,解决方法是将方法拆分到不同类中,或者通过其他方式手动控制缓存逻辑。

2、如何配置 Redis 缓存的序列化方式以避免序列化错误?

可以通过配置RedisCacheConfiguration 来设置序列化方式,首先创建StringRedisSerializer 实例用于序列化 key,然后创建Jackson2JsonRedisSerializer 实例用于序列化 value,并将其设置为 value 的序列化器,在配置类中注入该配置即可。

本站部分图片及内容来源网络,版权归原作者所有,转载目的为传递知识,不代表本站立场。若侵权或违规联系Email:zjx77377423@163.com 核实后第一时间删除。 转载请注明出处:https://blog.huochengrm.cn/gz/23554.html

分享:
扫描分享到社交APP
上一篇
下一篇