2021SC@SDUSC
本文在CSDN个人博客同步发出,地址CSDN博客
概要
根据之前的分析,fastjson可以简单分类成两类,序列化部分toJSONString
和反序列化部分parseObject
。反序列化的含义,指将JSON字符串(类型String
)转化为Java对象。
我负责的部分是parseArray
部分。简单来说,可以理解为把JSON字符串内包含的多个JSON对象转化为Java的List
类型对象。因为parseArray
的底层原理和parseObject
相同,所以我为了更好地开始parseArray
部分的研究,先开始了对parse
部分源码解析。
本文对fastjson中parseObject
方法的代码展开解析
现在,我们正式开始反序列化源码的探究
parse
的API调用方式
在以前的学习过程中,我深深感到使用各语言的debug工具,合理设置断点,步步跟随程序运行、适时查看内存变量数据可以有效帮助程序员学习不了解的代码。
我写了一个简单的程序,来调用fastjson的功能
下面是API调用程序
package test;
import com.alibaba.fastjson.JSON;
public class test {
public static void main(String[] args) {
People people1=new People();
People.setName("Sam");
People.setId(1);
People.setDescription("good");//调用JavaBean的Getter、Setter生成people1对象
String str= JSON.toJSONString(people);//调用序列化方法,生成JSONString
People people2=JSON.parseObject(str, People.class);//我的探究重点方法
}
}
其中,People
类必须是一个符合JavaBean的类
People.class
:
package test
public class People{
private String name;
private int id;
private String description;
public people(){};
Getter&Setter//JavaBean,可以使用IDE自动生成,不贅叙
}
使用IDE的debug工具,在JSON.parseObject
方法设置断点,查看内存中people2
的变量情况,发现people2内各属性值均与people1相同,说明fastjson成功将people1
序列化的JSONString反序列化。
正式开始
在本节,我将一步步从
parseObejct
深入到最底层的方法,研究相互之间的关系
1. parseObject()
这是fastjson反序列化的入口API方法,提供给用户直接调用。他的方法参数只有String
的JSONString和用户希望转化成的clazz
类。返回一个clazz
类的对象。
fastjson的API一直很简洁,对于普通用户来说,只需要知道序列化和反序列化的两个接口即可,内部的所有操作均由fastjson自己完成
public static <T> T parseObject(String text, Class<T> clazz) {
/** 传入JSONString,根据clazz转化,返回一个clazz类的对象 */
return parseObject(text, clazz, new Feature[0]);
}
特别说明,这里的clazz不是写错,而是程序员为了表示“类”的含义,又不能使用保留字
class
,约定使用clazz
代替
这个方法是以下所有方法的最终调用者,也是直接提供服务的方法。
这个反序列化接口可以处理对象包含的任意字段类型
2. 还是parseObject()
这个方法也是fastjson提供的API之一,也是上一个parseObject
方法调用的函数。这个方法不仅包含了JSONString和类,还多了features
数组,存放所有的反序列化特性(可以理解为configure
)
public static <T> T parseObject(String json, Class<T> clazz, Feature... features) {
return (T) parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features);
}
同样,他也调用了另外的parseObject
方法,虽然这样看起来跳转有点多,但是细想,这样的操作在程序中形成了明显的相互调用关系。
若某处出现错误,只需要修改极小部分的代码即可修复,一处错误只需要修改一个函数,不会发生大范围修改。
此方法是上文中被调用的方法,也是parseObject
系列函数出现的第一个写了实际内容的函数。前面的方法看似累赘,但它们的存在简化了API接口,极大减轻了初学者和轻度使用者的学习成本,毕竟一个简介的API是所有程序员的最爱。下面详细介绍代码。
这个方法规定了输入:
String
input
Type
clazz
*.parser.ParserConfig
config
*.parser.deserializer.ParseProcess
processor
int
featureValues
Feature[]
features
public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,int featureValues, Feature... features) {
if (input == null || input.length() == 0) {
return null;//避免对空字符串操作
}
/** 配置反序列化的特性features */
if (features != null) {
for (Feature feature : features) {
featureValues |= feature.mask;
}
}
/** new一个JSONparser,反序列化的数据类型由其查找所有具体类型的反序列化器 */
DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
if (processor != null) {
if (processor instanceof ExtraTypeProvider) {
parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
}
if (processor instanceof ExtraProcessor) {
parser.getExtraProcessors().add((ExtraProcessor) processor);
}
if (processor instanceof FieldTypeResolver) {
parser.setFieldTypeResolver((FieldTypeResolver) processor);
}
}
/** 使用反序列化实例parser转换对象 */
T value = (T) parser.parseObject(clazz, null);
parser.handleResovleTask(value);
parser.close();
return (T) value;
}
这个方法表示了反序列化的核心架构,是今后一个研究大重点。
反序列化函数parseObejct
定义了反序列化的大框架:
- 创建配置
config
,其中包括了所有的反序列化特性配置 - 添加反序列化的
processor
- 根据具体类型,查找反序列化实例,执行反序列化
整个反序列化的核心,在于第三步,如何查找到具体的反序列化实例,前两步都是为了设置各种特性化配置
我们继续看这一步(重点)
T value = (T) parser.parseObject(clazz, null);
3. <T> T parseObject(Type type, Object fieldName)
从这个方法开始,我们的反序列化之旅开始从大框架走向具体化,例如具体数据类型的反序列化实例匹配、继承了常见类的类的反序列化实例匹配等等。自此,我们正式开始了针对不同(具体)类型的反序列化进程。
此方法也是
parseObject
系列的最后一个方法,因为从此开始需要对每个不同类型的数据进行反序列化操作。
public <T> T parseObject(Type type, Object fieldName) {
int token = lexer.token();
/** 获取JSON字符串的第一个token */
if (token == JSONToken.NULL) {
lexer.nextToken();
return (T) TypeUtils.optionalEmpty(type);
}
/** 根据token确定JSON字符串某位置的类型(包含JSON对象开始结束等特殊位置的字符 */
if (token == JSONToken.LITERAL_STRING) {
/** 若类型为字节数据 */
if (type == byte[].class) {
byte[] bytes = lexer.bytesValue();
lexer.nextToken();
return (T) bytes;
}
/** 获取到char类型数据 */
if (type == char[].class) {
String strVal = lexer.stringVal();
lexer.nextToken();
return (T) strVal.toCharArray();
}
}
/** 使用config对特定类型的查找反序列化实例,核心方法之一 */
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
/** 检查JSONString的语法正确性 */
if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
throw new JSONException("syntax error,expect start with { or [,but actually start with "+ lexer.tokenName());
}
/** 属于JavaBeanDeserializer类型(fastjson的内部类型),简单处理 */
return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
} else {
/** 核心方法之二,执行反序列化 */
return (T) deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
throw new JSONException(e.getMessage(), e);
}
}
从这个方法的代码分析,很容易可以得出这样的结论:
反序列化关键在于根据不同的类型,查找到对应的反序列化实例;而这种类型匹配的思路是很巧妙的,在后续的介绍中会详细介绍fastjson团队的巧思。
最后
fastjson反序列化的部分暂时介绍到这里。本文从fastjson的API调用开始,使用IntelliJ IDEA
的debug工具,一步步深入fastjson代码,介绍到最后一个parseObject
方法。
- 从使用者的角度看,这些方法构成了一个对初学者友好的API接口,也为需要深度定制json转化服务的用户提供了可能
- 从代码角度来看,这些方法构成了一个易于维护、层级严密的调用层级关系。便于开发者调试,也便于开源代码学习者理解fastjson的代码逻辑
下次我计划从这里开始,分析ObjectDeserializer deserializer = config.getDeserializer(type);
这个方法如何处理不同的类
感谢各位老师的阅读指导!
fastjson真好用
fastjson真的好用