fastjson 解析多级泛型时而失败:
平时运行好好的,偶尔会反序列化泛型失败(实际是被转化成了JSONObject),堆栈如下
java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.mljr.hr.bean.Position
at com.mljr.hr.task.PositionSyncTask.batchNewData(PositionSyncTask.java:66)
at com.mljr.hr.task.AbstractScheduleTask.startScheduleNewData(AbstractScheduleTask.java:66)
at com.mljr.hr.task.PositionSyncTask.scheduleNewData(PositionSyncTask.java:56)
at com.mljr.hr.task.PositionSyncTask$$FastClassBySpringCGLIB$$b4e853c8.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at com.github.blackshadowwalker.spring.distributelock.interceptor.LockAspectSupport$1.invoke(LockAspectSupport.java:46)
at com.github.blackshadowwalker.spring.distributelock.interceptor.LockAspectSupport.execute(LockAspectSupport.java:89)
at com.github.blackshadowwalker.spring.distributelock.interceptor.LockAspectSupport.execute(LockAspectSupport.java:73)
at com.github.blackshadowwalker.spring.distributelock.interceptor.LockAspectSupport.around(LockAspectSupport.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
外包装对象定义
class MyResponse {
Boolean success;
Integer errCode;
String errDes;
T result;
}
声明:MyResponse
>
调试分析
经过多次调试,问题出现在这里 DefaultFieldDeserializer#parseField
@Override
public void parseField(DefaultJSONParser parser, Object object, Type objectType, Map fieldValues) {
if (fieldValueDeserilizer == null) {
getFieldValueDeserilizer(parser.getConfig());
}
if (objectType instanceof ParameterizedType) {
ParseContext objContext = parser.getContext();
objContext.type = objectType;
fieldType = FieldInfo.getFieldType(this.clazz, objectType, fieldType);
}
如我的对象类型 result 的类型是 List, 但是经过 getFieldValueDeserilizer(parser.getConfig()) 的处理后 fieldValueDeserilizer ==JavaObjectDeserializer
, 然后在 68行 进行解析处理
68 value = fieldValueDeserilizer.deserialze(parser, fieldType, fieldInfo.name);
接着分析 JavaObjectDeserializer
public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
if (componentType instanceof TypeVariable) {
TypeVariable> componentVar = (TypeVariable>) componentType;
componentType = componentVar.getBounds()[0];
}
List
然后进入 DefaultJSONParser 1280 行 parse(Object fieldName) ,在这里就将result的值解析成了 JSONArray对象,从而造成类型转换错误,没有得到预期的类型。
解决方案:
回到来这里 DefaultFieldDeserializer#parseField
@Override
public void parseField(DefaultJSONParser parser, Object object, Type objectType, Map fieldValues) {
if (fieldValueDeserilizer == null) {
getFieldValueDeserilizer(parser.getConfig());
}
if (objectType instanceof ParameterizedType) {
ParseContext objContext = parser.getContext();
objContext.type = objectType;
fieldType = FieldInfo.getFieldType(this.clazz, objectType, fieldType);
}
既然是多级泛型 ParameterizedType 并且解析除了泛型类型 fieldType ,为什么不加一行
fieldValueDeserilizer = parser.getConfig().getDeserializer(fieldType)
这样从我贴的数据中就得到了 fieldValueDeserilizer = CollectionCodec,这样就根据 fieldType获取到了正确的 fieldValueDeserilizer
1. 复现
过程:
1.定义
mType1 = new TypeReference>() {}.getType();
mType2 = new TypeReference>>() {}.getType();
class MyResponse {
Boolean success;
Integer errCode;
String errDes;
T result;
}
2.第一次调用
使用mType1
反序列化使用MyResponse
,
JSON.parseObject(jsonData, mType1, configBug569, featureValues,
features != null ? features : EMPTY_SERIALIZER_FEATURES);
结果:resp.getResult().getClass()==JSONArray.class
,同时fastjson会缓存 MyResponse的解析器(即目的类型为JSONArray)
3.第二次调用
使用mType2
反序列化使用的 MyResponse
>
JSON.parseObject(jsonData, mType2, configBug569, featureValues, features != null ? features : EMPTY_SERIALIZER_FEATURES);
结果:resp.getResult().getClass()==JSONArray.class
,与期望的List
不相符
2.分析
第一次调用反序列化,关键点: ParserConfig(460)
460 putDeserializer(type, derializer);
这里缓存了对于 MyResponse
的解析器
第二次调用反序列化:
ParserConfig(352-354行)
if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
derializer = derializers.get(clazz);
}
这里经过代码(337 derializers.get(type)
)没有找到指定type的解析器,然后到352行判断如果type是泛型,就使用class代替type从缓存中获取解析器,这样就获取到了MyResponse的解析器,而并非是MyResponse
的解析器。>
结论:
解析器缓存导致,然而ParserConfig默认都是使用 的 ParserConfig.getGlobalInstance(),从而缓存造成了影响。
3. 解决方案
fieldType = FieldInfo.getFieldType(this.clazz, objectType, fieldType);
,已经提交pull request且已经合并到’1.2.18-SNAPSHOT’。Issues:
- https://github.com/alibaba/fastjson/issues/569