1 Fastjson2简介
Fastjson2
是Fastjson
的升级版,特征:
- 协议支持:支持
JSON
/JSONB
两种协议 - 部分解析:可以使用
JSONPath
进行部分解析获取需要的值 - 语言支持:
Java
/Kotlin
- 场景支持:
Android8+
/服务端 - 其他特性支持:
Graal Native-Image
、JSON Schema
Fastjson漏洞原理
FastJson原理
2 基础使用
2.1 测试环境
环境:
JDK
版本:1.8.0_341
Fastjson2
版本:2.0.19
测试类:
java
@Builder @Data @ToString public class Entity { private String field1; private Integer field2; }
2.2 JSON
序列化
java
System.out.println(JSON.toJSONString(entity));
2.3 JSON
反序列化
java
System.out.println(JSON.parseObject(str,Entity.class));
2.4 JSONB
序列化
java
byte[] bytes = JSONB.toBytes(entity);
2.5 JSONB
反序列化
java
System.out.println(JSONB.parseObject(bytes,Entity.class));
2.6 JSON Schema
JSON Schema
可用于反序列化时对JSON
字段进行验证使用,配置Schema
可以通过@JSONField
/@JSONType
,这里以@JSONField
为例:
java
public class Entity { private String field1; @JSONField(schema = "{'minimum':0,'maximum':100}") private Integer field2; }
测试代码:
java
Entity entity1 = Entity.builder().field2(-1).build(); Entity entity2 = Entity.builder().field2(50).build(); Entity entity3 = Entity.builder().field2(101).build(); String str1 = JSON.toJSONString(entity1); String str2 = JSON.toJSONString(entity2); String str3 = JSON.toJSONString(entity3); try { JSON.parseObject(str1, Entity.class); } catch (Exception e) { e.printStackTrace(); } JSON.parseObject(str2, Entity.class); try { JSON.parseObject(str3, Entity.class); } catch (Exception e) { e.printStackTrace(); }
2.7 JSONPath
JSONPath
可用于部分解析JSON
字符串,示例:
java
Entity entity = Entity.builder().field1("a").field2(2).build(); // $符号表示根对象 // $.field1表示根对象的field1属性 System.out.println(JSONPath.eval(entity,"$.field1")); System.out.println(JSONPath.eval(entity,"$.field2")); List<Entity> list = new ArrayList<>(); list.add(Entity.builder().field1("entity1").field2(1).build()); list.add(Entity.builder().field1("entity2").field2(2).build()); // 如果传的是List,支持通过下标解析 // 此处是返回[0,0]下标范围内的值 List<Entity> names = (List<Entity>)JSONPath.eval(list,"[0,0]"); System.out.println(names.get(0));
详细的解析语法以及更多例子请参考官方文档。
2.8 AutoType
AutoType
是在序列化的时候带上类型的一种机制,这样在反序列化时就不需要传入类型,实现类型自动识别,例子:
java
Entity entity = Entity.builder().field1("a").field2(2).build(); String str = JSON.toJSONString(entity, JSONWriter.Feature.WriteClassName); System.out.println(str); System.out.println(JSON.parseObject(str, Object.class, JSONReader.Feature.SupportAutoType));
输出:
由于在Fastjson1
中出现过AutoType
漏洞,因此官方提供了一个JVM
参数完全禁止(safeMode
功能):
bash
-Dfastjson2.parser.safeMode=true
3 底层实现探究
3.1 序列化
3.1.1 概览
序列化的实现可以参考官方的一张类图:
大概流程如下:
- 获取
ObjectWriter
- 如果从
ObjectWriterProvider
缓存有ObjectWriter
,直接提取 - 如果
ObjectWriterProvider
缓存没有ObjectWriter
,构造对应的ObjectWriter
,并缓存 - 获取到
ObjectWriter
后,将JavaBean
对象写入JSONWriter
JSONWriter
对基础类型进行写入- 返回结果
3.1.2 入口
这里的序列化探究以JSON.toJSONString(Object object)
作为入口:
java
static String toJSONString(Object object) { // 初始化ObjectWriterProvider JSONWriter.Context writeContext = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider); // 格式化控制 boolean pretty = (writeContext.features & com.alibaba.fastjson2.JSONWriter.Feature.PrettyFormat.mask) != 0L; Object jsonWriter; // 默认有三个JSONWriter,JDK8一个,针对JDK9之后的byte[]实现的字符串优化也有一个,还有一个基于char[]实现的UTF16 if (JDKUtils.JVM_VERSION == 8) { jsonWriter = new JSONWriterUTF16JDK8(writeContext); } else if ((writeContext.features & com.alibaba.fastjson2.JSONWriter.Feature.OptimizedForAscii.mask) != 0L) { jsonWriter = new JSONWriterUTF8JDK9(writeContext); } else { jsonWriter = new JSONWriterUTF16(writeContext); } try { // 格式化控制 JSONWriter writer = pretty ? new JSONWriterPretty((JSONWriter)jsonWriter) : jsonWriter; String var12; try { if (object == null) { // null的话直接写入"null"字符串 ((JSONWriter)writer).writeNull(); } else { // 设置根对象 ((JSONWriter)writer).setRootObject(object); Class<?> valueClass = object.getClass(); if (valueClass == JSONObject.class) { // 如果目标类是JSNOObject,直接调用writer的write方法 ((JSONWriter)writer).write((JSONObject)object); } else { // 如果不是 JSONWriter.Context context = ((JSONWriter)writer).context; boolean fieldBased = (context.features & com.alibaba.fastjson2.JSONWriter.Feature.FieldBased.mask) != 0L; // 通过Provider获取ObjectWriter ObjectWriter<?> objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased); // ObjectWriter将数据写入JSONWriter objectWriter.write((JSONWriter)writer, object, (Object)null, (Type)null, 0L); } } // 结果 var12 = writer.toString(); // 下面的代码与序列化关系不大,可以不看 } catch (Throwable var10) { if (writer != null) { try { ((JSONWriter)writer).close(); } catch (Throwable var9) { var10.addSuppressed(var9); } } throw var10; } if (writer != null) { ((JSONWriter)writer).close(); } return var12; } catch (NumberFormatException | NullPointerException var11) { throw new JSONException("JSON#toJSONString cannot serialize '" + object + "'", var11); } }
3.1.3 获取ObjectWriterProvider
JSON.toJSONString()
入口:
java
JSONWriter.Context writeContext = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider);
其中会调用默认的构造方法初始化ObjectWriterProvider
:
java
public ObjectWriterProvider() { this.init(); ObjectWriterCreator creator = null; switch (JSONFactory.CREATOR) { case "reflect": creator = ObjectWriterCreator.INSTANCE; break; case "lambda": creator = ObjectWriterCreatorLambda.INSTANCE; break; case "asm": default: try { creator = ObjectWriterCreatorASM.INSTANCE; } catch (Throwable var5) { } if (creator == null) { creator = ObjectWriterCreatorLambda.INSTANCE; } } this.creator = (ObjectWriterCreator)creator; }
ObjectWriterCreator
采取的是单例模式,默认采用ASM
动态字节码实现。
3.1.4 获取ObjectWriter
有了ObjectWriterProvider
后,下一步就是获取ObjectWriter
,也就是JSON.toJSONString()
中的:
java
JSONWriter.Context context = ((JSONWriter)writer).context; boolean fieldBased = (context.features & com.alibaba.fastjson2.JSONWriter.Feature.FieldBased.mask) != 0L; ObjectWriter<?> objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
getObjectWriter()
如下:
java
public ObjectWriter getObjectWriter(Type objectType, Class objectClass, boolean fieldBased) { // fieldBased是基于字段序列化的意思 // false的话表示基于getter序列化 // 根据不同的类型从不同的缓存map中获取 ObjectWriter objectWriter = fieldBased ? (ObjectWriter)this.cacheFieldBased.get(objectType) : (ObjectWriter)this.cache.get(objectType); // 首次获取应该为null if (objectWriter != null) { return (ObjectWriter)objectWriter; } else { // 这个useModules布尔变量笔者不太了解 // 这里的逻辑是 基于字段反序列化 并且 目标class不为空 并且 目标class可以赋值给Iterable 并且 目标class不能赋值给class boolean useModules = true; if (fieldBased && objectClass != null && Iterable.class.isAssignableFrom(objectClass) && !Collection.class.isAssignableFrom(objectClass)) { useModules = false; } // 这里的例子是true if (useModules) { for(int i = 0; i < this.modules.size(); ++i) { // 获取ObjectWriterModule ObjectWriterModule module = (ObjectWriterModule)this.modules.get(i); objectWriter = module.getObjectWriter(objectType, objectClass); // 为null if (objectWriter != null) { ObjectWriter previous = fieldBased ? (ObjectWriter)this.cacheFieldBased.putIfAbsent(objectType, objectWriter) : (ObjectWriter)this.cache.putIfAbsent(objectType, objectWriter); if (previous != null) { objectWriter = previous; } return (ObjectWriter)objectWriter; } } } // 第一次执行暂时还拿不到ObjectWriter,这里的条件全部符合 if (objectWriter == null && objectClass != null && !fieldBased) { switch (objectClass.getName()) { // 针对Guava库的map内置了ObjectWriter case "com.google.common.collect.HashMultimap": case "com.google.common.collect.LinkedListMultimap": case "com.google.common.collect.LinkedHashMultimap": case "com.google.common.collect.ArrayListMultimap": case "com.google.common.collect.TreeMultimap": objectWriter = GuavaSupport.createAsMapWriter(objectClass); break; // 不是JSONObject类 case "com.alibaba.fastjson.JSONObject": objectWriter = ObjectWriterImplMap.of(objectClass); } } // ObjectWriter还没拿到 if (objectWriter == null) { // 第一次拿需要通过ObjectWriterCreator()去创建 ObjectWriterCreator creator = this.getCreator(); if (objectClass == null) { objectClass = TypeUtils.getMapping(objectType); } // 创建ObjectWriter // ObjectWriter的里面会创建FieldWriter,这里面的逻辑很长,经过一些列复杂逻辑的判断,再针对字段获取 objectWriter = creator.createObjectWriter(objectClass, fieldBased ? Feature.FieldBased.mask : 0L, this); // 放入缓存 ObjectWriter previous = fieldBased ? (ObjectWriter)this.cacheFieldBased.putIfAbsent(objectType, objectWriter) : (ObjectWriter)this.cache.putIfAbsent(objectType, objectWriter); if (previous != null) { objectWriter = previous; } } return (ObjectWriter)objectWriter; } }
其中FiledWriter
获取逻辑如下:
java
// ObjectWriterCreatorASM.java 236行左右的位置 BeanUtils.fields(objectClass, (field) -> { if (fieldBased || Modifier.isPublic(field.getModifiers())) { fieldInfo.init(); // 创建FieldWriter FieldWriter fieldWriter = this.creteFieldWriter(objectClass, writerFeatures, provider.modules, beanInfo, fieldInfo, field); if (fieldWriter != null) { // 放入缓存 fieldWriterMap.putIfAbsent(fieldWriter.fieldName, fieldWriter); } } });
3.1.5 write()
操作
获取到ObjectWriter
之后,就可以进行write()
操作了,JSON.toJSONString()
入口:
java
objectWriter.write((JSONWriter)writer, object, (Object)null, (Type)null, 0L);
由于自定义类的ObjectWriter
是运行时拿到的,无法通过调试获取到,但是可以通过内置的ObjectWriter
来判断大概的write()
流程:
比如LocalDateWriter
的write()
如下:
java
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { try { int year = (Integer)this.getYear.invoke(object); int monthOfYear = (Integer)this.getMonthOfYear.invoke(object); int dayOfMonth = (Integer)this.getDayOfMonth.invoke(object); Object chronology = this.getChronology.invoke(object); if (chronology != this.utc && chronology != null) { jsonWriter.startObject(); // 写入key jsonWriter.writeName("year"); // 写入value jsonWriter.writeInt32(year); jsonWriter.writeName("month"); jsonWriter.writeInt32(monthOfYear); jsonWriter.writeName("day"); jsonWriter.writeInt32(dayOfMonth); jsonWriter.writeName("chronology"); jsonWriter.writeAny(chronology); jsonWriter.endObject(); } else { LocalDate localDate = LocalDate.of(year, monthOfYear, dayOfMonth); DateTimeFormatter formatter = this.getDateFormatter(); if (formatter == null) { formatter = jsonWriter.getContext().getDateFormatter(); } if (formatter == null) { jsonWriter.writeLocalDate(localDate); } else { String str = formatter.format(localDate); jsonWriter.writeString(str); } } } catch (InvocationTargetException | IllegalAccessException var14) { throw new JSONException("write LocalDateWriter error", var14); } }
根据getter
获取字段值并调用对应的Writer
去写JSON
。
3.2 反序列化
3.2.1 概览
反序列化也可以参考官方的一张图:
大概流程与序列化类似:
- 获取
ObjectReader
- 如果
ObjectReaderProvider
有缓存,从缓存提取 - 如果
ObjectReaderProvider
没有缓存,创建ObjectReader
并且缓存到ObjectReaderProvider
中 - 通过
JSONReader
得到Object
- 返回结果
3.2.2 入口
java
Entity entity = Entity.builder().field1("a").field2(2).build(); String str = JSON.toJSONString(entity); System.out.println(JSON.parseObject(str,Entity.class));
其中parseObject()
如下:
java
static <T> T parseObject(String text, Class<T> clazz) { if (text != null && !text.isEmpty()) { // 获取JSONReader JSONReader reader = JSONReader.of(text); Object var7; try { JSONReader.Context context = reader.context; // 判断是否基于字段反序列化 boolean fieldBased = (context.features & Feature.FieldBased.mask) != 0L; // 通过ObjectReaderProvider获取ObjectReader // 这个context看起来和ObjectReaderProvider无关,实际上内部实现Context包含了ObjectReaderProvider ObjectReader<T> objectReader = context.provider.getObjectReader(clazz, fieldBased); // 反序列化核心方法 T object = objectReader.readObject(reader, (Type)null, (Object)null, 0L); if (reader.resolveTasks != null) { reader.handleResolveTasks(object); } if (reader.ch != 26 && (reader.context.features & Feature.IgnoreCheckClose.mask) == 0L) { throw new JSONException(reader.info("input not end")); } var7 = object; } catch (Throwable var9) { if (reader != null) { try { reader.close(); } catch (Throwable var8) { var9.addSuppressed(var8); } } throw var9; } if (reader != null) { reader.close(); } return var7; } else { return null; } }
3.2.3 获取JSONReader
JSON.parseObject()
入口:
java
JSONReader reader = JSONReader.of(text);
其中JSONReader.of()
实现如下:
java
public static JSONReader of(String str) { if (str == null) { throw new NullPointerException(); } else { // 创建Context // Context内部包含了ObjectReaderProvider Context context = JSONFactory.createReadContext(); int length; // 测试环境JDK8,此处if不成立 if (JDKUtils.JVM_VERSION > 8 && JDKUtils.UNSAFE_SUPPORT) { try { length = JDKUtils.STRING_CODER != null ? JDKUtils.STRING_CODER.applyAsInt(str) : UnsafeUtils.getStringCoder(str); if (length == 0) { byte[] bytes = JDKUtils.STRING_VALUE != null ? (byte[])JDKUtils.STRING_VALUE.apply(str) : UnsafeUtils.getStringValue(str); return new JSONReaderASCII(context, str, bytes, 0, bytes.length); } } catch (Exception var4) { throw new JSONException("unsafe get String.coder error"); } } length = str.length(); char[] chars; // 测试环境JDK8 if (JDKUtils.JVM_VERSION == 8) { // 获取char array chars = JDKUtils.getCharArray(str); } else { chars = str.toCharArray(); } // 由于JDK8的String还是使用char[]实现的,所以返回UTF16的JSONReader return new JSONReaderUTF16(context, str, chars, 0, length); } }
3.2.4 获取ObjectReader
JSON.parseObject()
入口:
java
ObjectReader<T> objectReader = context.provider.getObjectReader(clazz, fieldBased);
getObjectReader()
如下:
java
public ObjectReader getObjectReader(Type objectType, boolean fieldBased) { if (objectType == null) { objectType = Object.class; } // 有缓存直接从缓存提取 ObjectReader objectReader = fieldBased ? (ObjectReader)this.cacheFieldBased.get(objectType) : (ObjectReader)this.cache.get(objectType); // 第一次获取ObjectReader为null if (objectReader != null) { return objectReader; } else { Iterator var4 = this.modules.iterator(); ObjectReader previous; while(var4.hasNext()) { ObjectReaderModule module = (ObjectReaderModule)var4.next(); // 获取到的ObjectReader为null objectReader = module.getObjectReader(this, (Type)objectType); if (objectReader != null) { previous = fieldBased ? (ObjectReader)this.cacheFieldBased.putIfAbsent(objectType, objectReader) : (ObjectReader)this.cache.putIfAbsent(objectType, objectReader); if (previous != null) { objectReader = previous; } return objectReader; } } Type rawType; // 条件不符合 if (objectType instanceof TypeVariable) { Type[] bounds = ((TypeVariable)objectType).getBounds(); if (bounds.length > 0) { rawType = bounds[0]; if (rawType instanceof Class) { previous = this.getObjectReader(rawType, fieldBased); if (previous != null) { ObjectReader previous = this.getPreviousObjectReader(fieldBased, (Type)objectType, previous); if (previous != null) { previous = previous; } return previous; } } } } // 条件不符合 if (objectType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)objectType; rawType = parameterizedType.getRawType(); Type[] typeArguments = parameterizedType.getActualTypeArguments(); if (rawType instanceof Class) { Class rawClass = (Class)rawType; boolean generic = false; for(Class clazz = rawClass; clazz != Object.class; clazz = clazz.getSuperclass()) { if (clazz.getTypeParameters().length > 0) { generic = true; break; } } if (typeArguments.length == 0 || !generic) { ObjectReader rawClassReader = this.getObjectReader(rawClass, fieldBased); if (rawClassReader != null) { ObjectReader previous = this.getPreviousObjectReader(fieldBased, (Type)objectType, rawClassReader); if (previous != null) { rawClassReader = previous; } return rawClassReader; } } } } Class<?> objectClass = TypeUtils.getMapping((Type)objectType); String className = objectClass.getName(); if (objectReader == null && !fieldBased) { switch (className) { // 针对Guava的MultiMap,这里的条件不符合 case "com.google.common.collect.ArrayListMultimap": objectReader = ObjectReaderImplMap.of((Type)null, objectClass, 0L); } } // ObjectReader为null if (objectReader == null) { // 获取ObjectReaderCreator ObjectReaderCreator creator = this.getCreator(); // 创建ObjectReader objectReader = creator.createObjectReader(objectClass, (Type)objectType, fieldBased, this); } previous = this.getPreviousObjectReader(fieldBased, (Type)objectType, objectReader); if (previous != null) { objectReader = previous; } return objectReader; } }
在创建ObjectReader
的同时,会在其中创建FieldReader
:
java
// ObjectReaderCreator.java 453 FieldReader[] fieldReaderArray = this.createFieldReaders(objectClass, objectType, beanInfo, fieldBased, provider);
3.2.5 read()
操作
有了ObjectReader
之后就可以进行具体的read()
操作了,JSON.parseObject()
入口:
java
T object = objectReader.readObject(reader, (Type)null, (Object)null, 0L);
此处的readObject()
如下:
java
// ObjectReaderNoneDefaultConstructor.java 171行 public T readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) { // 判断是否带有JSONReader.Feature.ErrorOnNoneSerializable if (!this.serializable) { jsonReader.errorOnNoneSerializable(this.objectClass); } // 是否JSONB,不符合 if (jsonReader.isJSONB()) { return this.readJSONBObject(jsonReader, fieldType, fieldName, 0L); } else if (jsonReader.isArray() && jsonReader.isSupportBeanArray(features | this.features)) { // 是数组而且支持JSONReader.Feature.SupportArrayToBean,不符合 jsonReader.next(); LinkedHashMap<Long, Object> valueMap = null; for(int i = 0; i < this.fieldReaders.length; ++i) { Object fieldValue = this.fieldReaders[i].readFieldValue(jsonReader); if (valueMap == null) { valueMap = new LinkedHashMap(); } long hash = this.fieldReaders[i].fieldNameHash; valueMap.put(hash, fieldValue); } if (!jsonReader.nextIfMatch(']')) { throw new JSONException(jsonReader.info("array not end, " + jsonReader.current())); } else { jsonReader.nextIfMatch(','); return this.createInstanceNoneDefaultConstructor((Map)(valueMap == null ? Collections.emptyMap() : valueMap)); } } else { // 读取字符 boolean objectStart = jsonReader.nextIfObjectStart(); // 条件为false,进入else if (!objectStart && !jsonReader.isTypeRedirect() && jsonReader.nextIfEmptyString()) { return null; } else { // 获取Context,读取其中的Features JSONReader.Context context = jsonReader.getContext(); long featuresAll = this.features | features | context.getFeatures(); // 此处存储对象的值 // key是字段的long哈希值,哈希采取的是fnv1a 64算法生成的 // value是字段的具体值 LinkedHashMap<Long, Object> valueMap = null; // 读取 for(int i = 0; !jsonReader.nextIfMatch('}'); ++i) { // 读取字段名对应的hashCode long hashCode = jsonReader.readFieldNameHashCode(); if (hashCode != 0L) { if (hashCode == this.typeKeyHashCode && i == 0) { long typeHash = jsonReader.readTypeHashCode(); // 此处if条件不符合 if (typeHash != this.typeNameHash) { boolean supportAutoType = (featuresAll & Feature.SupportAutoType.mask) != 0L; ObjectReader autoTypeObjectReader; String typeName; if (supportAutoType) { autoTypeObjectReader = context.getObjectReaderAutoType(typeHash); if (autoTypeObjectReader == null) { typeName = jsonReader.getString(); autoTypeObjectReader = context.getObjectReaderAutoType(typeName, this.objectClass, this.features); } } else { typeName = jsonReader.getString(); autoTypeObjectReader = context.getObjectReaderAutoType(typeName, this.objectClass); } if (autoTypeObjectReader == null) { typeName = jsonReader.getString(); autoTypeObjectReader = context.getObjectReaderAutoType(typeName, this.objectClass, this.features); } if (autoTypeObjectReader != null) { Object object = autoTypeObjectReader.readObject(jsonReader, fieldType, fieldName, 0L); jsonReader.nextIfMatch(','); return object; } } } else { // 获取字段名哈希对应的FieldReader FieldReader fieldReader = this.getFieldReader(hashCode); // 条件不符合 if (fieldReader == null && (featuresAll & Feature.SupportSmartMatch.mask) != 0L) { long hashCodeLCase = jsonReader.getNameHashCodeLCase(); if (hashCodeLCase != hashCode) { fieldReader = this.getFieldReaderLCase(hashCodeLCase); } } // fieldReader不为null if (fieldReader == null) { this.processExtra(jsonReader, (Object)null); } else { // 读取字段值 Object fieldValue = fieldReader.readFieldValue(jsonReader); if (valueMap == null) { valueMap = new LinkedHashMap(); } long hash; if (fieldReader instanceof FieldReaderObjectParam) { hash = ((FieldReaderObjectParam)fieldReader).paramNameHash; } else { hash = fieldReader.fieldNameHash; } // 写入 valueMap.put(hash, fieldValue); } } } } // 构造对象 T object = this.createInstanceNoneDefaultConstructor((Map)(valueMap == null ? Collections.emptyMap() : valueMap)); if (this.setterFieldReaders != null && valueMap != null) { for(int i = 0; i < this.setterFieldReaders.length; ++i) { FieldReader fieldReader = this.setterFieldReaders[i]; // 读取字段值 Object fieldValue = valueMap.get(fieldReader.fieldNameHash); if (fieldValue != null) { // 通过setter注入 fieldReader.accept(object, fieldValue); } } } jsonReader.nextIfMatch(','); return object; } } }
3.3 ASM的作用和实现
众所周知,所有json解析工具中,Fastjson速度是最快的。而它能够快速对json字符串进行处理的原因之一,便是它使用了ASM框架。
Fastjson默认开启asm用来代替Java反射,提高了程序的整体性能。使用Java Bean测试发现开启asm情况下解析速度明显比不开要快。
下一篇我们分析asm在Fastjson中的实现。
ASM是什么
首先,我们把关注点放在ASM的定义上。
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
概括地说,ASM是一个能够不通过.java文件而直接修改.class文件的字节码操控框架。
Fastjson之所以速度快,原因之一是它使用了ASM。按照通常思路,反序列化应该是反射调用set方法进行属性设置。 这种方法是最简单的,但也是最低效的。而Fastjson使用ASM自己编写字节码,然后通过ClassLoader将字节码加载成类,避免了反射开销,大大增强了性能。
字节码格式
在此之前,了解一下Fastjson中ASM部分代码所在位置。
com.alibaba.fastjson.asm包中就是asm的全部代码。
要了解ASM的工作方式,先得了解字节码文件内部的格式。
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,就按照高位在前的方式分割成若干个8位字节进行存储。
Class文件格式采用类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:无符号数和表。
使用hexdump -C指令查看class文件的二进制代码
这里我们重点关注三个部分:魔数、版本号和常量池
魔数
class文件前8字节为魔数magic number,可以看到文件的前8位为cafe babe,正表示这是一个class类型的文件。
版本号
接下来的8字节是该文件的版本号,其中前4字节是次版本号minor version(00 00),后4字节是主版本号major version(00 34)。
常量池
接下来是常量池的入口,前4字节表示常量池容量,我这里是00 21,表示十进制的33,由于常量池计数从1开始,所以实际上常量的数量为32个。
常量池主要存储了字面量以及符号引用,其中字面量主要包括字符串,final常量的值或者某个属性的初始值等,而符号引用主要存储类和接口的全限定名称,字段的名称以及描述符,方法的名称以及描述符。
访问标志及其他
常量池之下是访问标志、字段表等class文件的其他信息,这里暂不一一表述。
通过命令行进入项目的target文件夹,找到编译后的class文件,执行指令javap -v Student.class对class文件反编译查看class文件结构。
这里截取一部分内容
可以看到反编译出来的class文件对每个常量进行了编号,一共32个常量。
ASM如何生成class文件
ASM使用访问者模式,动态对Java类进行修改。
在 ASM 中,提供了一个 ClassReader类,这个类可以直接由字节数组或由 class 文件间接的获得字节码数据,它能正确的分析字节码,构建出抽象的树在内存中表示字节码。它会调用 accept方法,这个方法接受一个实现了 ClassVisitor接口的对象实例作为参数,然后依次调用 ClassVisitor接口的各个方法。
public void accept(TypeCollector classVisitor) {
char[] c = new char[this.maxStringLength];
int anns = false;
int ianns = false;
int u = this.header;
int v = this.items[this.readUnsignedShort(u + 4)];
int len = this.readUnsignedShort(u + 6);
int w = false;
u += 8;
int i;
for(i = 0; i < len; ++i) {
u += 2;
}
i = this.readUnsignedShort(u);
int j;
for(v = u + 2; i > 0; --i) {
j = this.readUnsignedShort(v + 6);
for(v += 8; j > 0; --j) {
v += 6 + this.readInt(v + 2);
}
}
i = this.readUnsignedShort(v);
for(v += 2; i > 0; --i) {
j = this.readUnsignedShort(v + 6);
for(v += 8; j > 0; --j) {
v += 6 + this.readInt(v + 2);
}
}
i = this.readUnsignedShort(v);
for(v += 2; i > 0; --i) {
v += 6 + this.readInt(v + 2);
}
i = this.readUnsignedShort(u);
for(u += 2; i > 0; --i) {
j = this.readUnsignedShort(u + 6);
for(u += 8; j > 0; --j) {
u += 6 + this.readInt(u + 2);
}
}
i = this.readUnsignedShort(u);
//依次调用class Visitor的各个方法
for(u += 2; i > 0; --i) {
u = this.readMethod(classVisitor, c, u);
}
}
ClassReader知道如何对visitor的方法进行调用,而这个过程中用户无法干预。各个 ClassVisitor通过职责链模式,可以非常简单的封装对字节码的各种修改,而无须关注字节码的字节偏移,因为这些实现细节对于用户都被隐藏了,用户要做的只是重写相应的 visit 函数。
标准版的ASM包里面会有ClassAdaptor类,实现了Class Visitor的所有方法。但是Fastjson使用的是阉割版的ASM,只保留了必要的内容,降低了程序大小。Fastjson的ASM包里面只有一千余行代码。
ASM中另一个关键的类是ClassWriter,负责写字节码文件,以字节数组的形式输出。
ASM处理字节码的流程如下:
- 创建ClassReader对象,将.class文件的内容读入到字节数组中
- 创建ClassWriter对象,将处理后的字节数组回写入.class文件
- 使用时间过滤器ClassVisitor,对相应的方法修改并返回
ASM在Fastjson中的开启/关闭
ParserConfig.getGlobalInstance().setAsmEnable()方法来设置ASM的开启和关闭。
我们分别测试一下ASM开启和关闭时的性能,测试代码如下:
public void test02(){
String str="{\"age\":13,\"name\":\"james\"}";
boolean flag;
flag=true;
ParserConfig config=ParserConfig.getGlobalInstance();
config.setAsmEnable(flag);
long t1=System.currentTimeMillis();
Student student=JSON.parseObject(str,Student.class, Feature.AllowSingleQuotes);
long t2=System.currentTimeMillis();
System.out.println("ASM:"+flag);
System.out.println("time:"+(t2-t1));
System.out.println("age:"+student.getAge());
System.out.println("name:"+student.getName());
}
开启asm:
关闭asm:
可以看到,是否开启asm并不影响程序的结果,但是会影响程序的执行时间。开启asm下,程序执行时间明显比不开要短。这只是一个非常简单的json字符串的测试结果,在长json字符串的解析中,这一加速效果会更加明显。
4 结尾
其实文章中很多细节的地方由于篇幅的限制无法过于详细的解释,比如内置的各类型的Reader
/Writer
具体是如何获取值进行序列化/反序列操作的,想要深入探究这部分就只能自己去挖源码了。另外需要注意的是,文章的环境是在JDK8
下的,由于Fastjson2
在不同的JDK
下会有不同的序列化实现,因此仅供参考。
最后,关于性能的比较,可以参考官方的比较基准。