SpringBoot在使用@Cacheable缓存对象为空时遇到的坑

本文探讨了在使用@Cacheable注解时遇到的缓存空值问题,详细解析了错误信息并提供了两种解决方案:一是通过unless属性避免缓存空值,二是配置RedisCacheConfiguration允许缓存空值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天生产环境遇到@Cacheable的一个问题,记录一下

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.
java.lang.IllegalArgumentException: Cache ‘cache:getCustRange’ does not allow ‘null’ values. Avoid storing null via ‘@Cacheable(unless="#result == null")’ or configure RedisCache to allow ‘null’ via RedisCacheConfiguration

日志提示信息说的很清楚,缓存中不允许存储null值,在@Cacheable中加上(unless="#result == null")

或者在RedisCacheConfiguration中配置成允许缓存中存储null值

下面来看一下有问题的代码

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''")
  public CustRangeVo getCustRange(Map<String, Object> map) {
    return custMapper.getCustRange(map);
  }

很明显,是这里的CustRangeVo 对象为空了,保存到缓存时就报错了

2、解决问题

两种方法来解决这个问题

  • 设置结果为空时不缓存
  • 设置允许缓存为null值

设置结果为空时不缓存,直接加上unless="#result == null"就好了

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''", unless="#result == null")
  public CustRangeVo getCustRange(Map<String, Object> map) {
    return custMapper.getCustRange(map);
  }

注意,我这里的场景是99.9%会查询到数据,极端情况下对象才会为空

假如你的大部分场景查询都为空,你不缓存空的话,会导致大部分请求命中数据库,你的缓存加的就没有意义了

设置允许缓存为null值
需要配置RedisCacheConfiguration,我们看一下源码

public void put(Object key, @Nullable Object value) {

		Object cacheValue = preProcessCacheValue(value);
		//可以看到报这个错的地方有个条件判断,不允许value为空的情况下value为空值才会报这个错误
		if (!isAllowNullValues() && cacheValue == null) {

			throw new IllegalArgumentException(String.format(
					"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
					name));
		}

		cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
	}

那我们继续来看一下这个isAllowNullValues()

protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {

		//可以看到此处allowCacheNummValues最终取的是RedisCacheConfiguration的cacheNullValues
		super(cacheConfig.getAllowCacheNullValues());

		Assert.notNull(name, "Name must not be null!");
		Assert.notNull(cacheWriter, "CacheWriter must not be null!");
		Assert.notNull(cacheConfig, "CacheConfig must not be null!");

		this.name = name;
		this.cacheWriter = cacheWriter;
		this.cacheConfig = cacheConfig;
		this.conversionService = cacheConfig.getConversionService();
	}

所以你只要把RedisCacheConfiguration 的cacheNullValues设置为true,就可以缓存null值了

当然,我使用的方案是设置结果为空时不缓存,RedisCacheConfiguration 的方案我没有测试过

从文档上看应该是可行的,有兴趣的同学可以自己测试一下

如果感觉对你有些帮忙,想跟我一起学习,坚信技术改变世界,请关注Java天堂公众号,我会定期分享自己的学习成果,第一时间推送给你

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值