2021SC@SDUSC
概要
上文fastjson源码解析——反序列化(八)我们深入探索了针对规定了每个元素类型的JSON对象数组执行反序列化的操作顺序,使用大量对比,将单一类型数组反序列化的API与规定了类型的API比较,探索这两个重点API内部对JSON字符串、token
的使用等等异同点:单一类型的API内部逻辑较简单,可以看到一些融合度较高的方法,也可以直接看到对token
的使用;规定各元素类型的API逻辑相对复杂,且具有完备的程序层次结构,不会在代码浅层看到对token
的使用,更多在进行结构的规定,减少程序错误输入对某个元素反序列化的影响。
本文将继续从规定各元素类型的JSON对象数组反序列化方法入手,探究其在parseArray(String, Type[], ParserConfig)
方法的
parser.handleResovleTask(list);
方法调用及其内部的逻辑
1. handleResovleTask(Object)
方法
这个方法的返回值是void
,同时可以看到内部实质上是通用反序列化器parser
对Object
(也就是上文提到的list
)内部进行调整。
先看代码
public void handleResovleTask(Object value) {
if (resolveTaskList == null) {
// 防止传入空值导致后续操作出现未预料到的空指针异常
return;
}
for (int i = 0, size = resolveTaskList.size(); i < size; ++i) {
ResolveTask task = resolveTaskList.get(i); // 获取到parser的成员列表中的元素
String ref = task.referenceValue; // 保存操作的依据(值)
Object object = null;
if (task.ownerContext != null) {
object = task.ownerContext.object;
}
Object refValue;
if (ref.startsWith("$")) {
refValue = getObject(ref);
if (refValue == null) {
try {
JSONPath jsonpath = new JSONPath(ref, SerializeConfig.getGlobalInstance(), config, true);
if (jsonpath.isRef()) {
// 判断是否需要修改默认反序列化操作得到的结果列表
refValue = jsonpath.eval(value);
}
} catch (JSONPathException ex) {
// skip
}
}
} else {
refValue = task.context.object;
}
FieldDeserializer fieldDeser = task.fieldDeserializer;
if (fieldDeser != null) {
if (refValue != null
&& refValue.getClass() == JSONObject.class
&& fieldDeser.fieldInfo != null
&& !Map.class.isAssignableFrom(fieldDeser.fieldInfo.fieldClass)) {
Object root = this.contextArray[0].object;
JSONPath jsonpath = JSONPath.compile(ref);
if (jsonpath.isRef()) {
refValue = jsonpath.eval(root);
}
}
// workaround for bug
if (fieldDeser.getOwnerClass() != null
&& (!fieldDeser.getOwnerClass().isInstance(object))
&& task.ownerContext.parent != null
) {
for (ParseContext ctx = task.ownerContext.parent;ctx != null;ctx = ctx.parent) {
if (fieldDeser.getOwnerClass().isInstance(ctx.object)) {
object = ctx.object;
break;
}
}
}
fieldDeser.setValue(object, refValue);
}
}
}
这个方法的核心思路在于,根据通用反序列化器内部存储的列表,对执行了默认反序列化操作的JSON对象(存放于list中)进行深入检查,判断是否要修改数据列表的值。若不需要修改,则不动传入的list对象。如果需要修改,就根据反序列化器内部存储的依据值,对列表进行修改。
可以看到,在这个方法里出现了一个类型ResolveTask
,接下来我们进入这个类,查看此类型内部的数据存储及方法,帮助我们明确fastjson使用这个类执行哪些操作。
2. class ResolveTask
代码:
public static class ResolveTask {
public final ParseContext context;
public final String referenceValue;
public FieldDeserializer fieldDeserializer;
public ParseContext ownerContext;
public ResolveTask(ParseContext context, String referenceValue){
this.context = context;
this.referenceValue = referenceValue;
}
}
这个类是内部类,主要是保存一些有针对性的数据。针对JSON对象数组中的每一个元素所规定的对象类型,ResolveTask
类保存这个类型的反序列化数据,内部数据为public
,公开给其他方法,可直接查看数据,提供某个特定类型调整数据所需的参数、标志等。
作为一个存放数据的类型,ResolveTask
并不能对外提供任何方法,它只有一个带参数的构造函数。但是这个类的设计,从他的数据可见性设置,到构造函数,包括它内部类的属性,可以看出fastjson开发者对程序可读性、简洁性的独特考量。
- 内部类:设置这个类为内部类,而不是另外建立一个包内的类,说明这个类只会在
DefaultJSONParser
类内使用,其他类完全不可见,这样从根本上杜绝其他类非法调用/误调用ResolveTask
类的内容 -
ResolveTask
类的数据可见性:所有数据都是public
可见性,对所有调用可见。如果这个类是一个普通的类,它的数据可见性设置为public
是一个非常危险、极不安全的域。开发者的巧思就在其中,这个类是通用反序列化器的内部类,能够看到、调用它的只可能是通用反序列化器这一个类,本来不安全的权限设置,反而成了ResolveTask
类的优势,数据完全暴露给调用者,且数据不会暴露给其他任何类。这对于一个专门保存数据的类而言,既保证了数据的安全性、调用的合法性,也保证了调用数据的性能(getter方法获取数据、setter方法设置数值都属于方法调用,或多或少存在为了安全性牺牲了方法调用的开销),采用可直接赋值的方式。
我们可以看到,fastjson的开发者巧妙地设置了需要的类型的位置、数据可见性等等,尽可能提高性能的同时,保证了安全性,一举两得。
最后
本文对指定各元素类型的API内部逻辑parser.handleResovleTask(list)
开始,查看fastjson对反序列化的结果列表的修正方式。针对不同类型的元素,都保存了一个ResolveTask
类型的对象实例,存放这个类型的反序列化所需对象、数据等。
随着parseObject()
, parseArray()
这两个主要的操作分析进入尾声,我们一学期的fastjson源码解析之旅也接近终点,下次我将开始总结对象反序列化、数组反序列化的操作逻辑,从一个大的角度分析fastjson总体结构。
感谢各位老师的阅读与指导!