你好,我遇到了一个很奇怪的bug。方法列表。addAll会改变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;
}
我调用了方法,然后bug来了。第一次调用,我得到
(11、22)
这肯定是永远的答案。但是下一次,我得到
[22] 11日,22日,
和
[22日,11日,22日22],[22]11日,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());
谁能给我一个答案为什么,这是一个bug?还是错误地使用了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>
这不是缓存抽象本身的错误。您将返回一个缓存的可变集合,并允许调用者修改该集合。大多数缓存提供程序在将数据存储在缓存中之前都会对其进行序列化,这样条目就不会被修改。但是您可以配置缓存以使用对象引用。
如果返回一个可变对象并且使用对象引用,则对返回对象的任何修改也将影响该缓存条目。我知道这不是你想要的,有几种方法可以解决这个问题:
- 在你的合同中更清楚,并在
Collections.unmodifiableList
中包装你的列表-这是一个突破性的变化,但它使调用者更明显,如果他们想修改它,他们必须复制你的列表。 总是序列化内容,而不是存储对象引用。你没有告诉我们你的设置,但据我所知,你正在使用默认的
CacheManager
(即内存中的并发哈希映射)。它将按原样存储你的对象,所以你可能需要使用不同的缓存提供程序。注意,从4.3开始,您还可以配置缓存管理器来序列化内容而不是存储引用。