Bug描述
CoaOperServiceBO这个类中caoCache定义为私有的,修改缓存的方法没有被任何方法调用,但是莫名其妙造成coaCache被修改。
public class CoaOperServiceBO{
private Map coaCache = new HashMap();
public CoaDto getCoabyID(String coa_id) throws CoaException {
// zwh20141009key值中增加区划
CoaDto coaDto = null;
if (coaCache.get(getKeyRgSetYear() + coa_id) != null) {
coaDto = (CoaDto) coaCache.get(getKeyRgSetYear() + coa_id);
} else {
// 调用平提供的COA服务得到COA对象
UtilCache.removeCoaById(coa_id);
XMLData coaData = glcoa.getCoabyID(coa_id);
// coaDto = XMLdataToCoaObj(coaData);
// 把平台提供的对象转换为统一对外提供的COA对象
if (coaData != null) {
coaDto = ChangeXMLdataToCoaObj(coaData);
coaCache.put(getKeyRgSetYear() + coa_id, coaDto);
}
}
relatedElementDAO.replaceCoaAllEleName(coaDto.getCoaDetail());
//返回克隆对象,防止上层数据修改
return cloneCoaDto(coaDto);
}
...
}
发现
后面发现,将缓存中引用的对象直接放回给上一层。这样子,上层就相当于直接修改缓存了。然而上一层是不知道的,并且该类也有专门修改缓存的方法,逻辑思维上也不能通过上面这个方法直接修改缓存。于是通过查找方法的调用,具体找到了修改的地方。通过代码逻辑也确实是上层不清楚,导致缓存被修改。
解决方法
方案一
通过clone对象的方法,返回对象的副本,而不是直接返回缓存中指向的对象,这样调用者修改就不会造成缓存也被修改。这种适合处理不是特别复杂的对象
方案二
也是返回对象的副本,但是通过序列化转成字节流,再将字节流转为对象。这种适合处理比较复杂的对象
总结
这个bug看上去简单,但是稍不注意。如果遇到了复杂逻辑嵌套,就比如这次,我是在调用第4层才找到的,总共差不多找了300多个调用方法。所以慎用缓存
然后Java中也是有不可变集合的,可以考虑使用