Spring Cache + Caffeine使用中的坑——缓存数据修改导致缓存Key失效问题

问题描述

今天在项目中突然碰到一个问题:使用的缓存是Spring Cache + Caffeine,缓存在执行两次后,突然Key中定义的条件失效。代码如下:

public class CodeOutputService {
    // 注入当前类的对象
    @Resources
    private CodeOutputService codeOutputService;

    /**
     * 获取所有Leader的信息,如果返回list不为空,则加入缓存
     */
    @Cacheable(cacheNames = {"code_output"}, key = "#root.methodName", unless = "#result == null || #result.size() <= 0")
    public List<String> getLeaderListOfCode() {
        List<String> resultList = new ArrayList<>();
        // 此处省略处理逻辑
        ...
        return resultList;
    }

    /**
     * 获取满足条件的Leader的信息,如果返回list不为空,则加入缓存
     */
    @Cacheable(cacheNames = {"code_output"}, key = "methodName + T(String).valueOf(#showBoss)", unless = "#result == null || #result.size() <= 0")
    public List<String> getLeaderListOfCode(boolean showBoss) 
        // 如果缓存存在,这里会从缓存中获取
        List<String> allLeaderList = this.codeOutputService.getLeaderListOfCode();
        if (!showBoss) {
            // 用来存放准备从 allLeaderList 中移除的对象的List
            List<String> removeList = new ArrayList<>();
            allLeaderList.forEach(leader -> {
                // 做数据处理
                if ("张三".equals(leader)) {
                    removeList.add(leader);
                }
            });
            // 从 allLeaderList 中移除需要删除的对象
            allLeaderList.removeAll(removeList);
        }
        return allLeaderList;
    }
}

如代码所示。在项目中,每当传递的 showBoss 参数为 false 时,我发现,之后的代码执行结果中,不管showBoss 参数传递的是 true 或者是 false,getLeaderListOfCode(boolean showBoss) 接口返回的数据 都是 showBoss = false 的时候的结果。

通过代码调试,我发现,只要执行了 showBoss = false 后的代码逻辑,即只要执行了 allLeaderList.removeAll(removeList); 这段代码,如果 removeList 不为空,那么从内存缓存中取出的 allLeaderList 是已经移除掉 removeList 中元素后的List。

反思总结

其实仔细想想,也不难理解。所谓内存缓存,我们可以理解为我们缓存的数据都存在于一个 缓存框架 管理的类中,并且我们缓存的数据都作为了该类的一个属性。当我们尝试从缓存中取我们需要的值时,就是调用了该类的一个 get 方法。该类持有的是我们存储的数据的引用,我们从缓存中拿到的所需数据的引用。我们通过引用,修改数据时,修改的都是引用指向的实际数据本身。所以,我们修改了从缓存中取得的数据后,缓存中实际存储的数据也被修改了,我们再从缓存中取,取得的就是修改后的数据了。

其实这个很好理解,我说的可能还把问题复杂化了。

解决方案

那么,这个问题可以如何解决呢?这里,我提供一种解决方案:

@Cacheable(cacheNames = {"code_output"}, key = "methodName + T(String).valueOf(#showBoss)", unless = "#result == null || #result.size() <= 0")
public List<String> getLeaderListOfCode(boolean showBoss) 
    // 如果缓存存在,这里会从缓存中获取
    List<String> tempList = this.codeOutputService.getLeaderListOfCode();
    // 通过 new ArrayList<>(Collection<? extends E> c) 重新构建一个List,后续的修改都只修改这个List,而不动缓存中存储的那个List
    List<String> allLeaderList = new ArrayList<>(tempList);
    // 此处省略逻辑处理
    ...
    return allLeaderList;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值