2.3.8.2 反序列化过程
反序列化的含义是将跨语言的json字符串转换成java对象
思路
public static T parseObject(String text, Class clazz) { /** 根据指定text,返回期望的java对象类型class */ return parseObject(text, clazz, new Feature[0]); }
关键步骤 1创建解析配置ParserConfig对象,包括初始化内部反序列化实例和特性配置等。
2添加反序列化拦截器
3根据具体类型查找反序列化实例,执行反序列化转换
4解析对象内部引用
基本概念 词法分析是反序列化的重要基础,在其他框架druid、parsii等框架都用到了词法分析的技术,个人认为在讲反序列化之前讲词法分析更重要。
- token-词法标记 用于标识当前在解析过程中解析到的对象的一个标记,具体的值参考 JSONToken。比如 {,即表示当前正在解析的是一个类似map或对象的格式,而},则表示当前对象已经到底了。
- ch-当前字符 用于表示当前已经读取到的字符是什么,如 abc,当位置为1时,则当前字符为 b
- bp-解析字符位置 用于表示当前字符所位于原始字符串中的哪一个位置,与ch是相对应的,它始终表示最新的一个位置,如果需要记录一些历史位置。如字符串起始位置,数字起始位置等,则需要使用其它标记,如np。
- sbuf-字符缓冲 在解析字符串时的特殊存储区域,主要是用于解析转义字符时的临时存储区。即如果原字符串为 a\t,则实际解析的字符串应该为a\t,那么原字符串为3位长,解析之后为2位长。即需要另行存储。字符缓冲区如名所示,为一个字符数组,需要需要单独的定义来存储长度信息,如使用sp。
- sp-字符缓冲区位置 这个用于表示在字符缓冲区之间记录当前字符串(或数字串)等的长度信息,同时也等同于当前的一个位置(如果坐标从0开始)。
- np-数字解析位置 用于实际表示在解析到常量信息时起始点的标记位置。通过np + sp,即计算得出相应的区间值了。
解析规则 定义规则
- 字符串 以"开头,并且以"结束,在中间可以存在以\为转义符,后面接"的情况,如 """,认为是正确的。但 """",认为是错误的,其解析时在倒数第2个双引号时即结尾了。
- 数字整形 以0-9开始,并且以连续数字,末尾可以s,b,f,d,l等
- 数字小数 以0-9开始,默认为f,D或者,中间存在小数点
- bool值 必须是 true 或 false ,全小写
- 数组集合 以[ 开头,以]结尾,中间以任意,分隔的信息
- set 以Set开头的,后面接数组集合的信息
- treeSet 以TreeSet开头的,后面接数组集合的信息
- 对象map 以{开始,以}结尾,中间按key,value集合的信息.
- null值 必须为null
- 字段 字符串,或单引号字符串,或无引号字符串,无引号时,必须为a-zA-Z_开头,以a-zA-Z0-9_结束.
词法规则 按照上面的定义,实际上就是贪婪的匹配规则,一旦满足一个匹配规则,那么这个匹配就要继续下去,直到当前规则不能完成时,同时在下一个规则之间,使用特定的分隔符作连接。 如 字符串 {a:"123",b:[1,2,3]},即按以下规则进行 1.对象开始:{ 2.对象key(即字段):a 3.分隔符: : 4.对象value开始: 5.字符串开始: " 6.字符串:123 7.字符串结束: " 8.对象value:结束: 9.对象间分隔符:, 10.对象key: b 11.分隔符: : 12.对象value开始: 13.数组开始: [ 14.数组值1数字开始: 1 15.数组值1数字结束: 1, 16.数组分隔符: , 17.… 18.数组结束: ] 19.对象结束: }
词法解析 整个词法,即TOKEN流,是由类JSONLexerBase来负责完成的,其负责提供主要词法单元的解析和结果取值操作。相应方法对应关系如下所示
- 数字 scanNumber numberString intValue longValue floatValue doubleValue
- 字符串 scanString stringVal
- NULL值 scanNULL,scanNullOrNew
- Boolean值 scanTrue,scanFalse
语法解析 1结束符判定 在词法解析上,fastjson根据第1个有效字符判定相应的类型,然后直到该类型结束之后,立即采用该类型。如以{开头,则一定返回object类型。对于类型 "{key:value} other" 这种非正确字符串,fastjson采用尾判断规则,即有效对象解析完毕之后,判定结束符必须已经到达字符串尾。如果Token值不是EOF,则表示字符串出现问题,而提示相错误的信息。 具体的判定,对应方法 DefaultJSONParser中的close()方法。
2忽略类型 默认情况下,fastjson对于json类型分别采用 set,treeSet,jsonArray,jsonObject,int(包含int,long,biginteger),float(包含float,double,bigDecimal),string,null,true,false来进行解析。即除基本的value词法表示外,其它均使用通用类型来表示。即数组使用set和jsonArray,对象使用jsonObject。
3数组解析 对应方法 Object parse(Object fieldName)
case LBRACKET: JSONArray array = new JSONArray(); parseArray(array, fieldName); return array;
如上所示,语法规则 [ 开始,表示为数组,则定义jsonArray,然后将此引用传递给具体的解析数组的方法中,以进行处理。jsonArray可以理解为使用ArrayList封装的复合对象。上面的fieldName解析对象时标识相应的key值,这里默认为null。以下代码忽略非关键性处理.
public final void parseArray(final Collection array, Object fieldName) {
.....
final JSONLexer lexer = getLexer();
//因为当前位置为[ 跳转到下一个词法单元处
lexer.nextToken(JSONToken.LITERAL_STRING);
for (int i = 0;; ++i) {
......
Object value;
switch (lexer.token()) {
case LITERAL_INT:
//当前值为int,解析value值,并跳转到下一个标记处
value = lexer.integerValue();
//如当前字符串为 1,2 则解析完1之后,跳转到,处,以方便后面作判断,并跳转至2处
//这里的nextToken中的参数表示期望值,但实际上也不一定是该值,不过可以根据该值作一个进一步判定相应值。如这里期望,但也可以是一个 ],而表示解析结束
lexer.nextToken(JSONToken.COMMA);
break;
case LITERAL_FLOAT:
......//解析小数
case LITERAL_STRING:
......//解析字符串
break;
case TRUE://解析true
case FALSE://解析false
case LBRACE:
//这里碰到一个{,表示数组中还内嵌有对象,则跳转到解析对象的地方
JSONObject object = new JSONObject(isEnabled(Feature.OrderedField));
value = parseObject(object, i);
break;
case LBRACKET:
//这里碰到 [,则表示数组中还内嵌有数组,跳转到解析子数组的地方,这里的items值是新数组
Collection items = new JSONArray();
parseArray(items, i);
value = items;
break;
case NULL://解析null值
case UNDEFINED://js中的undefiend也认为是null值
case RBRACKET:
//这里碰到 ],则表示当前数组已经解析完毕,可以正常的return了。其它地方都是继续循环处理,即只有在这里才能正常跳出循环
lexer.nextToken(JSONToken.COMMA);
return;
case EOF:
//非正常跳出循环的地方,即字符串一下未匹配到] 就到末尾了
throw new JSONException("unclosed jsonArray");
default:
//默认解析
value = parse();
break;
}
//将上面解析到的对象添加到数组中
array.add(value);
//这里因为前面在解析完之后,均往前解析了一位,即期望碰到 ,这里判断如果是 ,值 表示还有后续的value值
//则继续往前解析,如 a,b 则期望当前位置从,处解析到b处
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(JSONToken.LITERAL_STRING);
continue;
}
}
类型处理 上面的为不带类型处理,则fastjson不知道应该将返回类型设置为什么类型。如果调用方主动的提示处理,则采用另一种处理方式,则根据提示类型对array以及object作处理。实际上,带类型处理的话,则会使用到ObjectDeserializer来进行解析。不过这里又会重新调用 DefaultJSONParser来进行处理。如对于(数组)或集合,则会使用 parseArray(Type type, Collection array, Object fieldName) 通过传递回来的collection对象,将相应值并根据type进行解析,再放回集合中。并根据返回类型进行调整。如数组,则会使用jsonArray进行二次转换来得到最终结果。代码参考如下:
public void parseArray(Type type, Collection array, Object fieldName) {
//这里根据type值获取具体类型的解析器
ObjectDeserializer deserializer = null;
if (int.class == type) {
deserializer = IntegerCodec.instance;
lexer.nextToken(JSONToken.LITERAL_INT);
}......
for (int i = 0;; ++i) {
......
if (int.class == type) {
Object val = IntegerCodec.instance.deserialze(this, null, null);
array.add(val);
}
......
//下一个元素解析
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(deserializer.getFastMatchToken());
continue;
}
}
可以看出,此处解析与通用解析逻辑基本一致,惟一不同的即是这里使用了针对类型的各种deserializer解析器来完成针对类型的工作。
面向对象封装 在实际的使用场景,我们均会使用到如 parseObject(String text, Class clazz) 来期望返回具体的类型,这里实际上就会调用到了不同的类型反序列化器了。fastjson根据这里的类型,调用相应的序列化对象来完成不同的对象解析工作。
1类型映射 而序列化器的工作也并不是进行具体的语法解析,而是提供相应的类型信息,以期望jsonParser进行正常的解析工作。即具体的解析工作仍是由jsonParser来完成,ObjectDeserializer只不过提供一些上下文信息,以及对流程进行控制。
从 ObjectDeserializer的继承上可以看出,存在很多不同的反序列化器。同时,对于fastjson内置的反序列化器,采用了 ParserConfig.derializers来进行内部存储。因为默认情况下,会使用parseConfig的单例对象,因此这里的存储是全局共享的(实际上也没有关系)
2数字解析 对应类为IntegerCodec
public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
final JSONLexer lexer = parser.getLexer();
Integer intObj;
if (lexer.token() == JSONToken.LITERAL_INT) {
//如果token匹配到数字,而直接通过integer.parse强转
int val = lexer.intValue();
lexer.nextToken(JSONToken.COMMA);
intObj = Integer.valueOf(val);
} else if (lexer.token() == JSONToken.LITERAL_FLOAT) {
//匹配到小数,而这里需要整数,而截取掉
BigDecimal decimalValue = lexer.decimalValue();
lexer.nextToken(JSONToken.COMMA);
intObj = Integer.valueOf(decimalValue.intValue());
} else {
//其它类型,采用类型转换强制转换,如匹配到字符串,也是可以转换为 整数的
Object value = parser.parse();
intObj = TypeUtils.castToInt(value);
}
//单独处理,使用IntegerCodec同时支持 int和 atomicInteger两种,算是偷懒吧
if (clazz == AtomicInteger.class) {
return (T) new AtomicInteger(intObj.intValue());
}
return (T) intObj;
}
3数组(集合)解析 对应类CollectionDeserializer 数组对应为ArrayDeserializer
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
//根据类型获取不同的集合实现
Class<?> rawClass = getRawClass(type);
//以下即根据不同的接口类 作不同的实现。因此在应用中,经常使用接口来标识不同的集合类
Collection list;
if (rawClass == AbstractCollection.class) {//ArrayList
} else if (rawClass.isAssignableFrom(HashSet.class)) {//HashSet
} else if (rawClass.isAssignableFrom(LinkedHashSet.class)) {//LinkedHashSet
} else if (rawClass.isAssignableFrom(TreeSet.class)) {//TreeSet
} else if (rawClass.isAssignableFrom(ArrayList.class)) {//List
} else if (rawClass.isAssignableFrom(EnumSet.class)) {//EnumSet
} else {//默认情况下,直接实例化
list = (Collection) rawClass.newInstance();
}
//这里尝试获取泛型实例信息,如 List<String> 则获取string,即期望集合中每一项值均是 字符串
Type itemType;
if (type instanceof ParameterizedType) {
itemType = ((ParameterizedType) type).getActualTypeArguments()[0];
} else {
itemType = Object.class;
}
//调用3.3中的不同类型解析公式处理
parser.parseArray(itemType, list, fieldName);
return (T) list;
}
4 javaBean封装解析
在实际解析过程中 javaBean与map的解析规则基本上一致。不过在map中的key值是任意的,而在javaBean中key值是固定的。即javaBean中可以控制在反序列化时哪些key是可接收的,哪些是不可接收的。
同时,javaBean通过配置项JsonField,可以重新配置字段的别名,从而映射到其它的key上。在map中, 均不存在相应的处理.
javaBean的过程可以理解为,先创建对象,然后每于{}中的每一项,先匹配key值,然后根据key值查找到相应的字段信息,根据不同的字段再解析该字段值。即语法表中的 pair: STRING ':' value ;
具体的解析代码如下所示(忽略非关键信息)
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, Object object) {
//简化判断,即针对原生json对象,使用类似map的解析规则
if (type == JSON.class || type == JSONObject.class) {
return (T) parser.parse();
}
//如果直接是一个null,则直接返回null即可,表示该对象不存在
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken(JSONToken.COMMA);
return null;
}
Map<String, Object> fieldValues = null;
//预处理,如果直接为 {},则表示是空对象(不是null,直接返回
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
object = createInstance(parser, type);
return (T) object;
}
......
for (;;) {
//查找字段值
String key = lexer.scanSymbol(parser.getSymbolTable());
......
//这里因为找到了具体的字段,则根据字段进行解析该字段信息
boolean match = parseField(parser, key, object, type, fieldValues);
//这里碰到了},表示对象已经解析结束,就不再处理了
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
break;
}
}
return (T) object;
源码
public static T parseObject(String text, Class clazz) { /** 根据指定text,返回期望的java对象类型class */ return parseObject(text, clazz, new Feature[0]); }
这个反序列化接口可以处理对象包含任意字段类型,但是自身不能是泛型类型,原因是java的运行时类型擦除。fastjson给出了替代方法解决:
String json = "[{},...]"; Type listType = new TypeReference<List>() {}.getType(); List modelList = JSON.parseObject(json, listType);
继续分析内部调用parseObject public static T parseObject(String json, Class clazz, Feature... features) { return (T) parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features); }
//反序列化的过程
public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
int featureValues, Feature... features) {
if (input == null || input.isEmpty()) {
return null;
}
/** 配置反序列化时启用的特性,比如是否允许json字符串字段不包含双引号 */
if (features != null) {
for (Feature feature : features) {
featureValues |= feature.mask;
}
}
/**
* 初始化DefaultJSONParser,反序列化类型由它
* 委托config查找具体序列化处理器处理
*/
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);
}
}
/** 使用反序列化实例转换对象,查找具体序列化实例委托给config查找 */
T value = (T) parser.parseObject(clazz, null);
/** 处理json内部引用协议格式对象 */
parser.handleResovleTask(value);
parser.close();
return (T) value;
}
继续查看parser.parseObject(clazz, null)逻辑:
public <T> T parseObject(Type type, Object fieldName) {
int token = lexer.token();
/** 获取json串第一个有效token */
if (token == JSONToken.NULL) {
/** 如果返回时null,自动预读下一个token */
lexer.nextToken();
return null;
}
/** 判定token属于字符串 */
if (token == JSONToken.LITERAL_STRING) {
if (type == byte[].class) {
/** 获取byte字节数据,分为十六进制和base64编码 */
byte[] bytes = lexer.bytesValue();
lexer.nextToken();
return (T) bytes;
}
/** 获取字符数组, 特殊处理String内存占用 */
if (type == char[].class) {
String strVal = lexer.stringVal();
lexer.nextToken();
return (T) strVal.toCharArray();
}
}
ObjectDeserializer deserializer = config.getDeserializer(type);
/** 委托config进行特定类型查找反序列化实例 */
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
}
/** 执行反序列化 */
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);
}
}
反序列化核心逻辑还是在委托配置查找反序列化实例,我们具体看看是如何查找反序列化实例的, 进入ParserConfig#getDeserializer(java.lang.reflect.Type)自己查看逻辑:
//查找反序列化实例
public ObjectDeserializer getDeserializer(Type type) {
/** 首先从内部已经注册查找特定class的反序列化实例 */
ObjectDeserializer deserializer = get(type);
if (deserializer != null) {
/** 引用类型,根据特定类型再次匹配 */
return deserializer;
}
if (type instanceof Class<?>) {
return getDeserializer((Class<?>) type, type);
}
if (type instanceof ParameterizedType) {
/** 获取泛型类型原始类型 */
Type rawType = ((ParameterizedType) type).getRawType();
/** 泛型原始类型是引用类型,根据特定类型再次匹配 */
if (rawType instanceof Class<?>) {
return getDeserializer((Class<?>) rawType, type);
} else {
/** 递归调用反序列化查找 */
return getDeserializer(rawType);
}
}
if (type instanceof WildcardType) {
/** 类型是通配符或者限定类型 */
WildcardType wildcardType = (WildcardType) type;
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds.length == 1) {
Type upperBoundType = upperBounds[0];
/** 获取泛型上界(? extends T),根据特定类型再次匹配 */
return getDeserializer(upperBoundType);
}
}
/** 如果无法匹配到,使用默认JavaObjectDeserializer反序列化 */
return JavaObjectDeserializer.instance;
}
反序列化匹配getDeserializer(Type)主要特定处理了泛型类型,取出泛型类型真实类型还是委托内部ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type)进行精确类型查找:
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
/** 首先从内部已经注册查找特定type的反序列化实例 */
ObjectDeserializer deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
if (type == null) {
type = clazz;
}
/** 再次从内部已经注册查找特定class的反序列化实例 */
deserializer = get(type);
if (deserializer != null) {
return deserializer;
}
{
JSONType annotation = TypeUtils.getAnnotation(clazz,JSONType.class);
if (annotation != null) {
Class<?> mappingTo = annotation.mappingTo();
/** 根据类型注解指定的反序列化类型 */
if (mappingTo != Void.class) {
return getDeserializer(mappingTo, mappingTo);
}
}
}
if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
/** 根据泛型真实类型查找反序列化实例 */
deserializer = get(clazz);
}
if (deserializer != null) {
return deserializer;
}
for (Module module : modules) {
deserializer = module.createDeserializer(this, clazz);
if (deserializer != null) {
putDeserializer(type, deserializer);
return deserializer;
}
}
/** 获取class名称,进行类型匹配(可以支持高版本jdk和三方库) */
String className = clazz.getName();
className = className.replace('$', '.');
if (className.startsWith("java.awt.") //
&& AwtCodec.support(clazz)) {
/**
* 如果class的name是"java.awt."开头 并且
* 继承 Point、Rectangle、Font或者Color 其中之一
*/
if (!awtError) {
String[] names = new String[] {
"java.awt.Point",
"java.awt.Font",
"java.awt.Rectangle",
"java.awt.Color"
};
try {
for (String name : names) {
if (name.equals(className)) {
/** 如果系统支持4中类型, 使用AwtCodec 反序列化 */
putDeserializer(Class.forName(name), deserializer = AwtCodec.instance);
return deserializer;
}
}
} catch (Throwable e) {
// skip
awtError = true;
}
deserializer = AwtCodec.instance;
}
}
if (!jdk8Error) {
try {
if (className.startsWith("java.time.")) {
String[] names = new String[] {
"java.time.LocalDateTime",
"java.time.LocalDate",
"java.time.LocalTime",
"java.time.ZonedDateTime",
"java.time.OffsetDateTime",
"java.time.OffsetTime",
"java.time.ZoneOffset",
"java.time.ZoneRegion",
"java.time.ZoneId",
"java.time.Period",
"java.time.Duration",
"java.time.Instant"
};
for (String name : names) {
if (name.equals(className)) {
/** 如果系统支持JDK8中日期类型, 使用Jdk8DateCodec 反序列化 */
putDeserializer(Class.forName(name), deserializer = Jdk8DateCodec.instance);
return deserializer;
}
}
} else if (className.startsWith("java.util.Optional")) {
String[] names = new String[] {
"java.util.Optional",
"java.util.OptionalDouble",
"java.util.OptionalInt",
"java.util.OptionalLong"
};
for (String name : names) {
if (name.equals(className)) {
/** 如果系统支持JDK8中可选类型, 使用OptionalCodec 反序列化 */
putDeserializer(Class.forName(name), deserializer = OptionalCodec.instance);
return deserializer;
}
}
}
} catch (Throwable e) {
// skip
jdk8Error = true;
}
}
if (!jodaError) {
try {
if (className.startsWith("org.joda.time.")) {
String[] names = new String[] {
"org.joda.time.DateTime",
"org.joda.time.LocalDate",
"org.joda.time.LocalDateTime",
"org.joda.time.LocalTime",
"org.joda.time.Instant",
"org.joda.time.Period",
"org.joda.time.Duration",
"org.joda.time.DateTimeZone",
"org.joda.time.format.DateTimeFormatter"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = JodaCodec.instance);
return deserializer;
}
}
}
} catch (Throwable e) {
// skip
jodaError = true;
}
}
if ((!guavaError) //
&& className.startsWith("com.google.common.collect.")) {
try {
String[] names = new String[] {
"com.google.common.collect.HashMultimap",
"com.google.common.collect.LinkedListMultimap",
"com.google.common.collect.LinkedHashMultimap",
"com.google.common.collect.ArrayListMultimap",
"com.google.common.collect.TreeMultimap"
};
for (String name : names) {
if (name.equals(className)) {
putDeserializer(Class.forName(name), deserializer = GuavaCodec.instance);
return deserializer;
}
}
} catch (ClassNotFoundException e) {
// skip
guavaError = true;
}
}
if (className.equals("java.nio.ByteBuffer")) {
putDeserializer(clazz, deserializer = ByteBufferCodec.instance);
}
if (className.equals("java.nio.file.Path")) {
putDeserializer(clazz, deserializer = MiscCodec.instance);
}
if (clazz == Map.Entry.class) {
putDeserializer(clazz, deserializer = MiscCodec.instance);
}
if (className.equals("org.javamoney.moneta.Money")) {
putDeserializer(clazz, deserializer = MonetaCodec.instance);
}
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
/** 使用当前线程类加载器 查找 META-INF/services/AutowiredObjectDeserializer.class实现类 */
for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,
classLoader)) {
for (Type forType : autowired.getAutowiredFor()) {
putDeserializer(forType, autowired);
}
}
} catch (Exception ex) {
// skip
}
if (deserializer == null) {
deserializer = get(type);
}
if (deserializer != null) {
return deserializer;
}
if (clazz.isEnum()) {
if (jacksonCompatible) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (TypeUtils.isJacksonCreator(method)) {
deserializer = createJavaBeanDeserializer(clazz, type);
putDeserializer(type, deserializer);
return deserializer;
}
}
}
Class<?> deserClass = null;
JSONType jsonType = TypeUtils.getAnnotation(clazz, JSONType.class);
if (jsonType != null) {
deserClass = jsonType.deserializer();
try {
/** 如果是枚举类型并使用了注解,使用注解指定的反序列化 */
deserializer = (ObjectDeserializer) deserClass.newInstance();
putDeserializer(clazz, deserializer);
return deserializer;
} catch (Throwable error) {
// skip
}
}
/** 如果是枚举类型,使用EnumSerializer反序列化 */
deserializer = new EnumDeserializer(clazz);
} else if (clazz.isArray()) {
/** 如果是数组类型,使用数组对象反序列化实例 */
deserializer = ObjectArrayCodec.instance;
} else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
|| clazz == ArrayList.class) {
/** 如果class实现集合接口,使用CollectionCodec反序列化 */
deserializer = CollectionCodec.instance;
} else if (Collection.class.isAssignableFrom(clazz)) {
deserializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
/** 如果class实现Map接口,使用MapDeserializer反序列化 */
deserializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
/** 如果class继承Throwable类,使用ThrowableDeserializer反序列化 */
deserializer = new ThrowableDeserializer(this, clazz);
} else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
deserializer = new PropertyProcessableDeserializer((Class<PropertyProcessable>) clazz);
} else if (clazz == InetAddress.class) {
deserializer = MiscCodec.instance;
} else {
/** 默认使用JavaBeanDeserializer反序列化(没有开启asm情况下) */
deserializer = createJavaBeanDeserializer(clazz, type);
}
/** 加入cache,避免同类型反复创建 */
putDeserializer(type, deserializer);
return deserializer;
}
总结 其实查找反序列化和之前提到了序列化类似,根据特定类型匹配接口或者继承实现类查找的 其实在整个过程中,最核心的部分在于词法分析和语法解析.另外由于业务的复杂性以及在编码时的代码优化,fastjson在一些具体实现时,有一些额外的处理,导致逻辑上不是很懂。但是,理解了词法和语法,其它的看起来就不是太难了。 其它
- JsonField 用于描述字段的序列信息及反序列信息,如重新设定name值,是否需要反序列化等.
- JsonType 用于控制指定的类进行反序列化时的信息,如使用其它类作为映射信息.
- JsonCreator 用于控制在反序列化时初始化object时,采用哪一个构造函数或者工厂方法
- ExtraTypeProvider 用于控制在javaBean序列化时,字段的类型信息重新设定。比如 字段类型为Object,可以通过这个接口重新设定它的具体类型.
- ExtraProcessor 用于控制在序列化及反序列化时,如果碰到不同的解析的字段时,可以通过此接口重新 进行处理。如 字段a,在{"b":"abc"},可以通过这个接口重新将字段b所对应的值映射到字段a上。
- ObjectDeserializer 最后可以自已定义反序列化器,通过ParseConfig.putDeserializer 来添加相应的反序列化器
2.3.9 springboot集成FastJSON
1.引jar
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
2.3.10 Fastjson中的优化
2.3.10.1 Fastjson中Serialzie的优化实现
1、自行编写类似StringBuilder的工具类SerializeWriter。 把java对象序列化成json文本,是不可能使用字符串直接拼接的,因为这样性能很差。比字符串拼接更好的办法是使用java.lang.StringBuilder。StringBuilder虽然速度很好了,但还能够进一步提升性能的,fastjson中提供了一个类似StringBuilder的类com.alibaba.fastjson.serializer.SerializeWriter。
SerializeWriter提供一些针对性的方法减少数组越界检查。例如public void writeIntAndChar(int i, char c) {},这样的方法一次性把两个值写到buf中去,能够减少一次越界检查。目前SerializeWriter还有一些关键的方法能够减少越界检查的,我还没实现。也就是说,如果实现了,能够进一步提升serialize的性能。
2、使用ThreadLocal来缓存buf。 这个办法能够减少对象分配和gc,从而提升性能。SerializeWriter中包含了一个char[] buf,每序列化一次,都要做一次分配,使用ThreadLocal优化,能够提升性能。
3、使用asm避免反射 获取java bean的属性值,需要调用反射,fastjson引入了asm的来避免反射导致的开销。fastjson内置的asm是基于objectweb asm 3.3.1改造的,只保留必要的部分,fastjson asm部分不到1000行代码,引入了asm的同时不导致大小变大太多。
4、使用一个特殊的IdentityHashMap优化性能。 fastjson对每种类型使用一种serializer,于是就存在class -> JavaBeanSerizlier的映射。fastjson使用IdentityHashMap而不是HashMap,避免equals操作。我们知道HashMap的算法的transfer操作,并发时可能导致死循环,但是ConcurrentHashMap比HashMap系列会慢,因为其使用volatile和lock。fastjson自己实现了一个特别的IdentityHashMap,去掉transfer操作的IdentityHashMap,能够在并发时工作,但是不会导致死循环。
5、缺省启用sort field输出 json的object是一种key/value结构,正常的hashmap是无序的,fastjson缺省是排序输出的,这是为deserialize优化做准备。
6、集成jdk实现的一些优化算法 在优化fastjson的过程中,参考了jdk内部实现的算法,比如int to char[]算法等等。
2.3.10.2 Fastjson中deserializer的主要优化实现
deserializer也称为parser或者decoder,fastjson在这方面投入的优化精力最多。 1、读取token基于预测。 所有的parser基本上都需要做词法处理,json也不例外。fastjson词法处理的时候,使用了基于预测的优化算法。比如key之后,最大的可能是冒号":",value之后,可能是有两个,逗号","或者右括号"}"。在com.alibaba.fastjson.parser.JSONScanner中提供了这样的方法: Java代码
从上面摘抄下来的代码看,基于预测能够做更少的处理就能够读取到token。 2、sort field fast match算法 fastjson的serialize是按照key的顺序进行的,于是fastjson做deserializer时候,采用一种优化算法,就是假设key/value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key从输入中读取出来。通过这个优化,使得fastjson在处理json文本的时候,少读取超过50%的token,这个是一个十分关键的优化算法。基于这个算法,使用asm实现,性能提升十分明显,超过300%的性能提升。 { "id" : 123, "name" : "魏加流", "salary" : 56789.79}
在上面例子看,虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了。
这种算法分两种模式,一种是快速模式,一种是常规模式。快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能。
在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token。
实现sort field fast match算法的代码在这个类[com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory|code.alibabatech.com/svn/fastjso… 这里是有一个用于演示sort field fast match算法的代码: code.alibabatech.com/svn/fastjso…
// 用于快速匹配的每个字段的前缀
char[] size_ = "\"size\":".toCharArray();
char[] uri_ = "\"uri\":".toCharArray();
char[] titile_ = "\"title\":".toCharArray();
char[] width_ = "\"width\":".toCharArray();
char[] height_ = "\"height\":".toCharArray();
// 保存parse开始时的lexer状态信息
int mark = lexer.getBufferPosition();
char mark_ch = lexer.getCurrent();
int mark_token = lexer.token();
int height = lexer.scanFieldInt(height_);
if (lexer.matchStat == JSONScanner.NOT_MATCH) {
// 退出快速模式, 进入常规模式
lexer.reset(mark, mark_ch, mark_token);
return (T) super.deserialze(parser, clazz);
}
String value = lexer.scanFieldString(size_);
if (lexer.matchStat == JSONScanner.NOT_MATCH) {
// 退出快速模式, 进入常规模式
lexer.reset(mark, mark_ch, mark_token);
return (T) super.deserialze(parser, clazz);
}
Size size = Size.valueOf(value);
// ... ...
// batch set
Image image = new Image();
image.setSize(size);
image.setUri(uri);
image.setTitle(title);
image.setWidth(width);
image.setHeight(height);
return (T) image;
3、使用asm避免反射 deserialize的时候,会使用asm来构造对象,并且做batch set,也就是说合并连续调用多个setter方法,而不是分散调用,这个能够提升性能。
4、对utf-8的json bytes,针对性使用优化的版本来转换编码。 这个类是com.alibaba.fastjson.util.UTF8Decoder,来源于JDK中的UTF8Decoder,但是它使用ThreadLocal Cache Buffer,避免转换时分配char[]的开销。 ThreadLocal Cache的实现是这个类com.alibaba.fastjson.util.ThreadLocalCache。第一次1k,如果不够,会增长,最多增长到128k。
Java代码
6、symbolTable算法
我们看xml或者javac的parser实现,经常会看到有一个这样的东西symbol table,它就是把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。这是也是parse性能优化的关键算法之一。
以下是摘抄自JSONScanner类中的代码,这段代码用于读取类型为enum的value。
Java代码
int hash = 0;
for (;;) {
ch = buf[index++];
if (ch == '\"') {
bp = index;
this.ch = ch = buf[bp];
strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash); // 通过symbolTable来获得缓存好的symbol,包括fieldName、enumValue
break;
}
hash = 31 * hash + ch; // 在token scan的过程中计算好hash
// ... ...
}
2.3.11 简易版的Fastjson
public static void main(String[] args) throws IllegalAccessException {
// 创建需要转换的对象
Person person = new Person();
person.setName("小明");
person.setAge(17);
person.setSex("男");
// 将对象转换为Json字符串
String jsonString = MyJson.toJsonString(person);
// 打印到控制台
System.out.println(jsonString);
}
public static final String toJsonString(Object object) throws IllegalAccessException {
// 获取反射对象
Class<?> clazz = object.getClass();
// 获取反射对象的所有属性,包括私有属性
Field[] fields = clazz.getDeclaredFields();
// 创建字符串类,便于拼接出JSON字符串
StringBuffer stringBuffer = new StringBuffer();
String str = "";
stringBuffer.append("{");
// 遍历所有属性
for (Field field : fields) {
// 第一次无须拼接逗号
if (stringBuffer.length() != 1) {
stringBuffer.append(",");
}
// 给私有属性授权,以便能够对于私有属性操作
field.setAccessible(true);
stringBuffer.append("\"");
stringBuffer.append(field.getName());
stringBuffer.append("\"");
stringBuffer.append(":");
// 两种判断属性值是否为字符串类型的方式
Boolean flag = field.getType().isInstance(str);
// Boolean flag = field.getType().toString().equals("class java.lang.String");
if (flag) { // 如果属性的类型为字符串,则给属性值加上双引号
stringBuffer.append("\"");
stringBuffer.append(field.get(object));
stringBuffer.append("\"");
} else {
stringBuffer.append(field.get(object));
}
}
stringBuffer.append("}");
return stringBuffer.toString();
}
虽然造轮子的过程可能比较复杂,但我只需要编写一次,之后无论是什么对象我就可以直接使用。
3.参考文献
zhuanlan.zhihu.com/p/62763428 www.iflym.com/index.php/c…
4.总结
学习手稿
在学习过程中如果觉得电脑不足以让你有清楚的思路,那么可以把它写下来,这样会有意想不到的收获。
公众号
博客地址
blog.csdn.net/weixin_4156…
公众号
请关注 程序员面试之道