之前说过一个xstream使用过程中遇到的一个小问题,链接: https://my.oschina.net/110NotFound/blog/2987017
这次又遇到一个,情况是这样的:
当在生产环境使用xstream
进行解析xml转换为java对象的时候有一个缓存问题, 如果有两个不相干的接口,这两个接口唯一的共同点就是使用了xstream解析返回结果,且巧合的是两个接口的返回结果xml格式一模一样。
xml结果格式如下:
<response><success>true</success></response>
这两个接口都定义了一个bean,且结构一样,A对象定义如下:
@XStreamAliasType(value="response")
public class Response1 {
@XStreamAsAttribute
private String success;
@XStreamAsAttribute
private String errorMsg;
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
B对象定义如下:
@XStreamAliasType(value="response")
public class Response2 {
@XStreamAsAttribute
private String success;
@XStreamAsAttribute
private String errorMsg;
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
解析的操作大致如下:
public static void main(String[] args) throws InterruptedException {
String resp = "<response><success>true</success></response>";
Response1 response1 = (Response1) XmlUtil.xml2Object(resp, new Class[] { Response1.class });
String a = response1.getSuccess();
Object object = XmlUtil.xml2Object(resp, new Class[] { Response2.class });
Response2 response2 = (Response2) object;
String b = response2.getSuccess();
System.out.println(a+ " : " +b);
}
xmlUtil:
public class XmlUtil {
public static Object xml2Object(String inputXml, Class<?>[] types){
if (StringUtils.isBlank(inputXml)) {
return null;
}
XStream xstream = XStreamUtil.getInstance();
xstream.processAnnotations(types);
return xstream.fromXML(inputXml);
}
}
当A对象和B对象都在从xml解析到对象的时候会引发一个错误:
com.xxx.Response2 cannot be cast to com.xxx.Response1
根本原因: XStream本身在进行讲xml转换到对象的时候,对每一次操作的类都进行了对象缓存,存储在一个map里面,这个map的key是你要解析的xml的头结点名字,在这里就是response, 当解析xml的时候会读取这个map里面的key为response的value,这样可以直接在缓存里面拿到class,达到解析速度更快。 这就是报错的根本原因。
xstream里的com.thoughtworks.xstream.mapper.CachingMapper
类源码是这样定义的:
private transient Map realClassCache;
public Class realClass(String elementName) {
Object cached = realClassCache.get(elementName);
if (cached != null) {
if (cached instanceof Class) {
return (Class)cached;
}
throw (XStreamException)cached;
}
try {
Class result = super.realClass(elementName);
realClassCache.put(elementName, result);
return result;
} catch (ForbiddenClassException e) {
realClassCache.put(elementName, e);
throw e;
} catch (CannotResolveClassException e) {
realClassCache.put(elementName, e);
throw e;
}
}
可以看出使用 elementName 做缓存,这一点我个人觉得还是有待商榷,为了性能埋下了一个坑。