Spring 缓存中的错误?为什么 list.addAll 对结果有影响?

Bug in Spring cache? Why list.addAll make an effection on the result?

嗨,我遇到了一个非常奇怪的错误。 list.addAll 方法确实改变了 return 缓存中的 Spring 结果。

我说的简单点,代码是这样的。界面是这样的

public interface CacheService {
    List<Integer> getIntegers(String key);
}

然后是实现。

@Cacheable(cacheNames = {"cache"},unless = "#result.isEmpty()")
public List<Integer> getIntegers(String key){
    List<Integer> list = new ArrayList<>();
    list.add(Integer.valueOf(key));
    return list;
}

我在 Rest Controller 中测试它。

@Autowired
CacheService cacheService;
@RequestMapping("cache")
public List<Integer> cache(){
    List<Integer> listA= cacheService.getIntegers("11");
    listA.addAll(cacheService.getIntegers("22"));
    return listA;
}

我调用了这个方法然后错误来了。 第一次打电话,我得到

[11,22]

这肯定是永远的答案。 但下一次,我得到

[11,22,22]

然后

[11,22,22,22],[11,22,22,22,22]...

我很确定 listA.addAll(cacheService.getIntegers("22")); 是原因。一旦我如下更改代码,一切正常。

List<Integer> listA = cacheService.getIntegers("11");
List<Integer> listB = cacheService.getIntegers("22");
return Stream.of(listA,listB).flagMap(Collection::stream).collect(Collectors.toList());

谁能告诉我为什么,这是一个错误吗?或者是 addAll 的错误使用? 我正在使用 spring-boot,依赖项就像

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

这不是缓存抽象中的错误本身。您正在 returning 一个缓存的可变集合,并让调用者修改该集合。大多数缓存提供程序会在将数据存储在缓存中之前将数据序列化,这样条目就不会被修改。但是您可以将缓存配置为使用对象引用。

如果您 return 一个可变对象并使用对象引用,则对 return 对象的任何修改也会影响该缓存条目。我知道这不是你想要的,有几种方法可以解决这个问题:

  • 在你的合同中更加清楚,并将你的列表包装在 Collections.unmodifiableList - 这是一个重大的变化,但它让调用者更加明显,如果他们想要修改它,他们必须复制你的列表。
  • 始终序列化内容而不是存储对象引用。您没有告诉我们太多关于您的设置的信息,但据我所知,您使用的是默认的 CacheManager (即内存中的并发哈希映射)。这将存储您的对象 "as is",因此您可能想要使用不同的缓存提供程序。请注意,从 4.3 开始,您还可以 configure that cache manager to serialize the content rather than storing a reference.