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

错误信息: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、序列化错误

错误信息:例如使用 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、缓存配置错误

错误信息:如使用 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 的序列化器,在配置类中注入该配置即可。