號稱引領(lǐng)下一個十年的高性能JSON庫fastjson2登場了
你知道的越多,不知道的就越多,業(yè)余的像一棵小草!
你來,我們一起精進!你不來,我和你的競爭對手一起精進!
編輯:業(yè)余草
juejin.cn/post/7115219049931341854
推薦:https://www.xttblog.com/?p=5349
概述
FastJson2是FastJson項目的重要升級,目標(biāo)是為下一個十年提供一個高性能的JSON庫。根據(jù)官方給出的性能來看,相比v1版本,確實有了很大的提升,本篇文章我們來看下究竟做了哪些事情,使得性能有了大幅度的提升。
本篇將采用代碼測試 + 源碼閱讀的方式對FastJson2的性能提升做一個較為全面的探索。
一、環(huán)境準(zhǔn)備
首先,我們搭建一套用于測試的環(huán)境,這里采用 Springboot 項目,分別創(chuàng)建兩個 module:fastjson和fastjson2。使用兩個版本進行對比試驗。
代碼結(jié)構(gòu)如下所示:

1.1 引入對應(yīng)依賴
在父pom當(dāng)中引入一些我們需要使用的公共依賴,這里為了簡便,使用了
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
在 fastjson 當(dāng)中引入 fastjson 的依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
在 fastjson2 當(dāng)中引入 fastjson2 的依賴:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.8</version>
</dependency>
1.2 創(chuàng)建測試類
這里為了方便,直接使用main方法進行測試。
創(chuàng)建類:Student.java
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Student {
private String name;
private Integer age;
private String address;
public Student(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
創(chuàng)建測試main方法:
/**
* 定義循環(huán)次數(shù)
*/
private final static Integer NUM = 100;
public static void main(String[] args) {
// 總時間
long totalTime = 0L;
//初始化學(xué)生數(shù)據(jù)
List<Student> studentList = new ArrayList<>();
// 10w學(xué)生
for (int i = 0; i < 100000; i++) {
studentList.add(Student.builder().name("我犟不過你").age(10).address("黑龍江省哈爾濱市南方區(qū)哈爾濱大街267號").build());
}
// 按指定次數(shù)循環(huán)
for (int i = 0; i < NUM; i++) {
// 單次循環(huán)開始時間
long startTime = System.currentTimeMillis();
// 遍歷學(xué)生數(shù)據(jù)
studentList.forEach(student -> {
// 序列化
String s = JSONObject.toJSONString(student);
//字符串轉(zhuǎn)回java對象
JSONObject.parseObject(s, Student.class);
});
// 將學(xué)生list序列化,之后轉(zhuǎn)為jsonArray
JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(studentList));
// 將jsonArray轉(zhuǎn)java對象list
jsonArray.toJavaList(Student.class);
//單次處理時間
long endTime = System.currentTimeMillis();
// 單次耗時
totalTime += (endTime - startTime);
System.out.println("單次耗費時間:" + (endTime - startTime) + "ms");
}
System.out.println("平均耗費時間:" + totalTime / NUM + "ms");
}
上述代碼在fastjson和fastjson2的測試中基本相同,唯一不同在于在fastjson2當(dāng)中,jsonArray.toJavaList方法轉(zhuǎn)變成了jsonArray.toList。
二、性能測試
本節(jié)將使用上面的代碼進行測試。在此之前,我們首先需要針對兩個子工程設(shè)置相同的堆空間大小128M,以免造成偏差:

2.1 第一次測試
下面正是開始測試:
fastjson結(jié)果
單次耗費時間:863ms
單次耗費時間:444ms
單次耗費時間:424ms
單次耗費時間:399ms
單次耗費時間:384ms
單次耗費時間:355ms
單次耗費時間:353ms
單次耗費時間:363ms
... ...
單次耗費時間:361ms
單次耗費時間:356ms
單次耗費時間:355ms
單次耗費時間:357ms
單次耗費時間:351ms
單次耗費時間:354ms
平均耗費時間:366ms如上所示,除了第一次很慢,第二次變快,到最后基本穩(wěn)定在360毫秒左右,最終的平均耗時是366ms。
fastjson2結(jié)果
單次耗費時間:957ms
單次耗費時間:803ms
單次耗費時間:468ms
單次耗費時間:435ms
單次耗費時間:622ms
單次耗費時間:409ms
單次耗費時間:430ms
··· ···
單次耗費時間:400ms
單次耗費時間:641ms
單次耗費時間:403ms
單次耗費時間:398ms
單次耗費時間:431ms
單次耗費時間:356ms
單次耗費時間:362ms
單次耗費時間:626ms
單次耗費時間:404ms
單次耗費時間:395ms
平均耗費時間:478ms如上所示,首次執(zhí)行慢,逐步變快,但是后面就出現(xiàn)問題了,怎么執(zhí)行的時間這么不穩(wěn)定?跨度從390多到640多?這是怎么回事?平均時間也達到了478ms,反而比fastjson還要慢。
2.2 fastjson2慢的原因?
比較熟悉java的應(yīng)該都能想到一個問題:「由于堆空間大小不夠,導(dǎo)致頻繁發(fā)生GC,最終導(dǎo)致處理時間增長?」
帶著這個推測,我們使用 jvisualVM 來看下在fastjson2執(zhí)行時,內(nèi)存的使用情況,使用如下方式啟動:

如上所示的啟動放肆?xí)苯哟蜷_ jvisualvm 的控制面板,選擇 Visual GC,最終結(jié)果如下所示:

如上所示有幾處重點,單獨看下:
GC次數(shù)

如上所示,總共GC了1814次,耗時34.089s,最后一次失敗的原因是內(nèi)存分配失敗。
Full GC

如上所示,老年代發(fā)生了316次GC,耗時27.225s。
通過上面的觀察,基本可以確定由于「GC導(dǎo)致了fastjson2整體處理時間變長」。
2.3 fastjson的GC表現(xiàn)
我們可以再看下fastjson當(dāng)中的gc是什么樣的:
GC次數(shù)

如上可知,fastjson1中發(fā)生了1675次gc,與fastjson2相比少了139次,并且時間少了11.55s。
通過前面測試的結(jié)果,fastjson1平均時間366ms,而fastjson2是478ms,分別乘以100次,能夠得到如下的時間差:
(478?100?366?100)/1000 =11.2(478*100 - 366*100)/1000 = 11.2
與gc時間差11.55相差無幾,那么我們可以得到一個結(jié)論:「fastjson2的性能表現(xiàn),與堆空間的大小相關(guān)!」
2.4 第二次試驗
我們似乎得到了一個結(jié)論,但是如何確定是fastjson2的那個方法消耗更多的內(nèi)存空間呢?畢竟我們在測試方法中,調(diào)用了很多的方法。
所以我們進一步調(diào)小內(nèi)存,看看是否會有內(nèi)存溢出呢?
我們將內(nèi)存調(diào)整為64M:
-Xms64m -Xmx64m
運行后發(fā)現(xiàn)果然出現(xiàn)了內(nèi)存溢出,并且明確的指出是堆空間內(nèi)存溢出:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at com.alibaba.fastjson2.JSONReader.read(JSONReader.java:1274)
at com.alibaba.fastjson2.JSON.parseArray(JSON.java:1494)
at com.alibaba.fastjson2.JSONArray.parseArray(JSONArray.java:1391)
at com.wjbgn.fastjson2.test.TestFastJson2.main(TestFastJson2.java:43)
通過如上的異常堆棧,發(fā)現(xiàn)異常出現(xiàn)在測試代碼的43行:

提供debug發(fā)現(xiàn)最終異常出現(xiàn)在如下代碼:

結(jié)論:「在toJsonString方法時,發(fā)生了內(nèi)存溢出異常。」
2.5 第三次實驗
下面我們將內(nèi)存增大,看看是否能夠提升fastjson2的性能。將堆空間大小調(diào)整為256M。
fastjson
單次耗費時間:805ms
單次耗費時間:224ms
單次耗費時間:235ms
單次耗費時間:228ms
單次耗費時間:222ms
... ...
單次耗費時間:191ms
單次耗費時間:196ms
單次耗費時間:193ms
單次耗費時間:194ms
單次耗費時間:192ms
平均耗費時間:198ms如上所示,發(fā)現(xiàn)隨著堆空間增加,fastjson1有較大的性能提升,平均時長在198ms。
fastjson2
單次耗費時間:671ms
單次耗費時間:496ms
單次耗費時間:412ms
單次耗費時間:405ms
單次耗費時間:315ms
單次耗費時間:321ms
... ...
單次耗費時間:337ms
單次耗費時間:326ms
平均耗費時間:335ms如上所示,結(jié)果在335毫秒,隨著內(nèi)存增加,性能有提升,但是仍然沒有fastjson1快。
通過如上的實驗,我們似乎可以得到如下的結(jié)論:「在數(shù)據(jù)量較大時,fastjson的性能還要好于fastjson2!」
2.6 第四次試驗
本次測試我們要給足夠大堆空間,看看這兩者的性能表現(xiàn),此處將堆空間設(shè)置成1g:
-Xms1g -Xmx1g
fastjson
單次耗費時間:943ms
單次耗費時間:252ms
單次耗費時間:156ms
單次耗費時間:155ms
... ...
單次耗費時間:119ms
單次耗費時間:114ms
單次耗費時間:108ms
單次耗費時間:133ms
單次耗費時間:115ms
平均耗費時間:133ms如上所示,在足夠大的內(nèi)存條件下,fastjson的平均時間達到了133ms。
fastjson2
單次耗費時間:705ms
單次耗費時間:199ms
單次耗費時間:172ms
... ...
單次耗費時間:101ms
單次耗費時間:124ms
單次耗費時間:96ms
平均耗費時間:119ms如上所示,fastjson2處理速度首次高于fastjson。
2.7 小結(jié)
通過前面的測試,我們能夠得到如下的結(jié)論:
fastjson2相比fastjson確實是有性能提升,但是取決于堆內(nèi)存的大小。
堆空間小的情況下,fastjson的性能表現(xiàn)優(yōu)于fastjson2。
在適當(dāng)?shù)那闆r先,對jvm進行調(diào)優(yōu),是對應(yīng)用程序的性能有影響的
我們需要知道,堆空間并非越大越好,空間越大代表著GC處理時間會越長,其表現(xiàn)為應(yīng)用響應(yīng)時間的增加。
三、源碼分析
本節(jié)將通過閱讀源碼的方式簡單了解fastjson2的原理,主要分為兩個方面進行閱讀:
writer reader
「為什么通過這兩個方面?」
fastjson的核心就是將java對象序列化成json(對應(yīng)writer),以及將json反序列化成java對象(對應(yīng)reader)。而且其內(nèi)部正是通過這樣的命名方式去實現(xiàn)的。
3.1 序列化 writer
toJSONString方法
其實所謂的序列化,就是JSONObject.toJSONString的體現(xiàn),所以我們通過跟蹤其源碼去發(fā)現(xiàn)其原理,注意我寫注釋的位置。
/**
* Serialize Java Object to JSON {@link String} with specified {@link JSONReader.Feature}s enabled
*
* @param object Java Object to be serialized into JSON {@link String}
* @param features features to be enabled in serialization
*/
static String toJSONString(Object object, JSONWriter.Feature... features) {
// 初始化 【ObjectWriterProvider】 ,關(guān)注【JSONFactory.defaultObjectWriterProvider】
JSONWriter.Context writeContext = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider, features);
boolean pretty = (writeContext.features & JSONWriter.Feature.PrettyFormat.mask) != 0;
// 初始化jsonwriter,ObjectWriter會將json數(shù)據(jù)寫入jsonwriter
JSONWriterUTF16 jsonWriter = JDKUtils.JVM_VERSION == 8 ? new JSONWriterUTF16JDK8(writeContext) : new JSONWriterUTF16(writeContext);
try (JSONWriter writer = pretty ?
new JSONWriterPretty(jsonWriter) : jsonWriter) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
boolean fieldBased = (writeContext.features & JSONWriter.Feature.FieldBased.mask) != 0;
// 獲取ObjectWriter
ObjectWriter<?> objectWriter = writeContext.provider.getObjectWriter(valueClass, valueClass, fieldBased);
// ObjectWriter將數(shù)據(jù)寫入JSONWriter
objectWriter.write(writer, object, null, null, 0);
}
return writer.toString();
}
}
defaultObjectWriterProvider對象
查看JSONFactory.defaultObjectWriterProvider的內(nèi)容:
public ObjectWriterProvider() {
init();
// 初始化【ObjectWriterCreator】,用來創(chuàng)建【ObjectWriterProvider】
ObjectWriterCreator creator = null;
switch (JSONFactory.CREATOR) {
case "reflect": //反射
creator = ObjectWriterCreator.INSTANCE;
break;
case "lambda": // lambda
creator = ObjectWriterCreatorLambda.INSTANCE;
break;
case "asm":
default:
try {//asm
creator = ObjectWriterCreatorASM.INSTANCE;
} catch (Throwable ignored) {
// ignored
}
if (creator == null) {
creator = ObjectWriterCreatorLambda.INSTANCE;
}
break;
}
this.creator = creator;
}
如上所示,我們看到此處初始化了ObjectWriterCreator,其實現(xiàn)方式默認是「基于ASM的動態(tài)字節(jié)碼」實現(xiàn)。
另外還提供了 反射 和 lambda 的方式。
到此為止已經(jīng)獲取到了ObjectWriterProvider,它的作用是用來獲取ObjectWriter的。
getObjectWriter方法
ObjectWriter的作用就是將java對象寫入到j(luò)son當(dāng)中,所以我們下面開始關(guān)注這一行代碼的實現(xiàn):
writeContext.provider.getObjectWriter(valueClass, valueClass, fieldBased);
繼續(xù)查看getObjectWriter方法,查看關(guān)鍵位置代碼:
if (objectWriter == null) {
// 獲取creator,此處獲取的是方法開始時默認的【ObjectWriterCreatorASM】
ObjectWriterCreator creator = getCreator();
if (objectClass == null) {
objectClass = TypeUtils.getMapping(objectType);
}
// 此處創(chuàng)建ObjectWriter,內(nèi)部創(chuàng)建【FieldWriter】
objectWriter = creator.createObjectWriter(
objectClass,
fieldBased ? JSONWriter.Feature.FieldBased.mask : 0,
modules
);
ObjectWriter previous = fieldBased
? cacheFieldBased.putIfAbsent(objectType, objectWriter)
: cache.putIfAbsent(objectType, objectWriter);
if (previous != null) {
objectWriter = previous;
}
}
createObjectWriter方法
查看creator.createObjectWriter偽代碼:
// 遍歷java對象當(dāng)中的getter方法,獲取屬性名
BeanUtils.getters(objectClass, method -> {
... ...
String fieldName;
if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) {
if (record) {
fieldName = method.getName();
} else {
// 根據(jù)getter獲取到屬性名稱
fieldName = BeanUtils.getterName(method.getName(), beanInfo.namingStrategy);
}
} else {
fieldName = fieldInfo.fieldName;
}
... ...
在上面的getterName方法獲取到對象的屬性名,找到屬性后,創(chuàng)建對應(yīng)的【FieldWriter】:
//創(chuàng)建該屬性的fieldWriter
FieldWriter fieldWriter = createFieldWriter(
objectClass,
fieldName,
fieldInfo.ordinal,
fieldInfo.features,
fieldInfo.format,
fieldInfo.label,
method,
writeUsingWriter
);
// 將屬性名作為key,fieldWriter作為value放入緩存【fieldWriterMap】
FieldWriter origin = fieldWriterMap.putIfAbsent(fieldName, fieldWriter);
循環(huán)過所有的getter方法后,會得到一個全部屬性的List fieldWriters集合:
fieldWriters = new ArrayList<>(fieldWriterMap.values());
再往后,fastjson2會組裝一個動態(tài)類:【ObjectWriter_1】,在里面組裝能夠?qū)懭隞SONWriter的各種屬性和方法,以及get屬性獲取:

定義和初始化此對象的方法如下所示:
//定義【ObjectWriter_1】的屬性
genFields(fieldWriters, cw);
// 定義【ObjectWriter_1】的方法
genMethodInit(fieldWriters, cw, classNameType);
//定義【ObjectWriter_1】獲取對象屬性的讀取方法
genGetFieldReader(
fieldWriters,
cw,
classNameType,
new ObjectWriterAdapter(objectClass, null, null, features, fieldWriters)
);
此動態(tài)對象的末尾【1】是隨數(shù)量增長的。
繼續(xù)向下跟蹤到如下方法:
genMethodWrite(objectClass, fieldWriters, cw, classNameType, writerFeatures);
此方法主要的作用是創(chuàng)建【ObjectWrite_1】的write方法,并匹配當(dāng)前java對象的屬性屬于哪種類型,使用哪種FieldWriter進行寫入。
其內(nèi)部會輪詢所有的屬性進行匹配,我們的屬性主要是String和Integer,如下:
... ...
else if (fieldClass == Integer.class) {
// 處理Integer屬性
gwInt32(mwc, fieldWriter, OBJECT, i);
} else if (fieldClass == String.class) {
// 處理String屬性
gwFieldValueString(mwc, fieldWriter, OBJECT, i);
}
... ...
Integer 在內(nèi)部處理時,會在動態(tài)對象生成名稱是
writeInt32的方法。String 內(nèi)部處理時在動態(tài)對象生成方法
writeString。
再向下會通過以下方法修改寫入不同類型屬性的方法名稱和描述信息等
genMethodWriteArrayMapping("writeArrayMapping", objectClass, writerFeatures, fieldWriters, cw, classNameType);
能夠看到,Integer和String的后續(xù)處理方法不同:
String
else if (fieldClass == String.class) {
methodName = "writeString";
methodDesc = "(Ljava/lang/String;)V";
}
Integer 則是對象 "(Ljava/lang/Object;)V"
到此整個ObjectWriter_1對象就設(shè)置完成了,使用反射進行創(chuàng)建:
try {
Constructor<?> constructor = deserClass.getConstructor(Class.class, String.class, String.class, long.class, List.class);
return (ObjectWriter) constructor.newInstance(objectClass, beanInfo.typeKey, beanInfo.typeName, writerFeatures, fieldWriters);
} catch (Throwable e) {
throw new JSONException("create objectWriter error, objectType " + objectClass, e);
}
回到toJSONString方法
至此我們已經(jīng)拿到j(luò)ava對象的屬性,并成功創(chuàng)建了【ObjectWriter】:

再返回toJSonString方法當(dāng)中,看看Object的后續(xù)操作 拿到的ObjectWriter調(diào)用其【write】方法進行數(shù)據(jù)寫入:
objectWriter.write(writer, object, null, null, 0);
我們已經(jīng)知道不同類型屬性使用不同的FieldWriter進行寫入:
String:我們雖然提到過使用的
writeString方法,但是你會發(fā)現(xiàn)沒有對應(yīng)的FieldWriter,因為它使用的是JSONWriterUTF16JDK8的writeString(String str)方法,不同版本的jdk有不同的Class。Integr:使用
FieldWriterInt32的writeInt32(JSONWriter jsonWriter, int value)進行寫入。
關(guān)于具體的寫入過程就不在介紹了。
小結(jié)
官方提供Writer關(guān)系圖如下:

本節(jié)主要針對主要流程進行梳理,與上圖對比存在部分未講解流程,感興趣同學(xué)參照源碼自行閱讀。
整個過程較為復(fù)雜,簡單描述為:「使用ASM動態(tài)字節(jié)碼方式作為基礎(chǔ),通過java對象的getter方法獲取對象的屬性值,構(gòu)建動態(tài)ObjectWriter對象,針對不同的對象屬性,生成不同的寫入方法,最終通過反射進行對象創(chuàng)建,最后進行java對象數(shù)據(jù)的寫入。」
「值得一提的是,ObejctWriter對象是會進行緩存的,有助于性能的提升。」
3.2 反序列化 reader
下面來看看反序列化reader的流程。因為大體流程與writer差不多,所以以下內(nèi)容不做詳細講解了。
parseObject 方法
/**
* json轉(zhuǎn)換java對象
*
* @param text json字符串
* @param 需要轉(zhuǎn)換的類
* @return Class
*/
@SuppressWarnings("unchecked")
static <T> T parseObject(String text, Class<T> clazz) {
if (text == null || text.isEmpty()) {
return null;
}
//創(chuàng)建reader,內(nèi)部與writer相同,使用ASM動態(tài)字節(jié)碼形式創(chuàng)建creater
try (JSONReader reader = JSONReader.of(text)) {
// 獲取上下文
JSONReader.Context context = reader.context;
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
// 獲取ObjectReader
ObjectReader<T> objectReader = context.provider.getObjectReader(clazz, fieldBased);
T object = objectReader.readObject(reader, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
JSONReader.of方法
創(chuàng)建reader對象,
public static JSONReader of(String str) {
if (str == null) {
throw new NullPointerException();
}
//創(chuàng)建reader的上下文,內(nèi)部與writer相同,使用ASM動態(tài)字節(jié)碼形式創(chuàng)建creater,包裝成context
Context context = JSONFactory.createReadContext();
// jdk8以上版本使用下面的字符串處理方式
if (JDKUtils.JVM_VERSION > 8 && JDKUtils.UNSAFE_SUPPORT && str.length() > 1024 * 1024) {
try {
byte coder = UnsafeUtils.getStringCoder(str);
if (coder == 0) {
byte[] bytes = UnsafeUtils.getStringValue(str);
return new JSONReaderASCII(context, str, bytes, 0, bytes.length);
}
} catch (Exception e) {
throw new JSONException("unsafe get String.coder error");
}
return new JSONReaderStr(context, str, 0, str.length());
}
// jdk 8 及以下字符串處理
final int length = str.length();
char[] chars;
if (JDKUtils.JVM_VERSION == 8) {
// jdk8字符串轉(zhuǎn)char
chars = JDKUtils.getCharArray(str);
} else {
chars = str.toCharArray();
}
// 創(chuàng)建JSONReaderUTF16對象
return new JSONReaderUTF16(context, str, chars, 0, length);
}
getObjectReader方法
與getObjectWriter類似,獲取動態(tài)的json數(shù)據(jù)讀取對象。關(guān)注重點代碼:
if (objectReader == null) {
// 獲取前面創(chuàng)建的creater
ObjectReaderCreator creator = getCreator();
// 創(chuàng)建ObjectReader對象,根據(jù)java類的類型
objectReader = creator.createObjectReader(objectClass, objectType, fieldBased, modules);
}
createObjectReader方法
關(guān)注下面這行代碼:
// 創(chuàng)建屬性讀取對象數(shù)組
FieldReader[] fieldReaderArray = createFieldReaders(objectClass, objectType, beanInfo, fieldBased, modules);
繼續(xù)跟進,發(fā)現(xiàn)遍歷java對象的setter方法,此時我們應(yīng)該能夠想到,向?qū)ο笤O(shè)置值的時候,一定是使用的setter方法:
BeanUtils.setters(objectClass, method -> {
fieldInfo.init();
// 創(chuàng)建Fieldreader
createFieldReader(objectClass, objectType, namingStrategy, orders, fieldInfo, method, fieldReaders, modules);
});
createFieldReader方法會獲取java對象當(dāng)中的屬性,以及set開頭的方法。
處理完對象的屬性和set方法后,會生成ObjectReader對象進行返回:

此對象包含setterFieldReaders,用于向java對象寫入數(shù)據(jù)。
回到parseObject
下面看如何讀取json數(shù)據(jù)到j(luò)ava對象:
object = objectReader.readObject(reader, 0);
object內(nèi)部主要是循環(huán)遍歷fieldReaders,它內(nèi)部包含json當(dāng)中的屬性和對象的set方法:

正是通過這些屬性和set方法將json的數(shù)據(jù)放到j(luò)ava對象當(dāng)中。
首先將對象的屬性和值放到map當(dāng)中:
valueMap.put(fieldReader.getFieldNameHash(), fieldValue);
通過下面的方法將map轉(zhuǎn)換成java對象:
T object = createInstanceNoneDefaultConstructor(
valueMap == null
? Collections.emptyMap()
: valueMap);
內(nèi)部通過構(gòu)造器和值去創(chuàng)建一個新的java對象:
return (T) constructor.newInstance(args);
「注意:因為這個原因,在java對象當(dāng)中必須要有一個相應(yīng)的帶有參數(shù)的構(gòu)造器,否則會報錯。」
到此為止就成功拿到轉(zhuǎn)換后的java對象了。
小結(jié)
官方提供的Reader關(guān)系圖:

感興趣的同學(xué)可以參考上圖的內(nèi)容,結(jié)合本文提供的流程,自己跟蹤一遍源碼。
整個過成簡單描述:「底層使用ASM動態(tài)字節(jié)碼為基礎(chǔ),通過java對象的setter方法去構(gòu)建動態(tài)的ObjectReader對象,最終通過構(gòu)造器去創(chuàng)建一個新的java對象」。
四、總結(jié)
關(guān)于fastjson2的簡單測試,以及源碼閱讀到此就告一段落了。
針對fastjson2有以下幾點總結(jié):
fastjson2對于fastjson的兼容,可以使用下面的依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.8</version>
</dependency>
但是官方也不保證100%兼容。
內(nèi)存占用,通過前面的測試,發(fā)現(xiàn)fastjson2有明顯占用更大內(nèi)存的現(xiàn)象,甚至在相同內(nèi)存條件下,fastjson1可以完美執(zhí)行,而fastjson2有產(chǎn)生「內(nèi)存溢出」的風(fēng)險。
Issues
https://github.com/alibaba/fastjson2/issues通過官方的Issues能夠發(fā)現(xiàn)目前的bug還是比較多的,對于需要穩(wěn)定性的項目還是不建議嘗試。具體表現(xiàn)如下:

源碼閱讀難度,這個是我最想吐槽的,全部源碼幾乎沒有注釋信息,讀起來還是比較晦澀的。作者希望讀者能夠通過PR的方式補充注釋,也希望更多讀者加入進來,目前關(guān)于Fastjson2的源碼閱讀文章基本為0。

拋開上述存在的問題,fastjson2確實有不錯的性能提升,通過官方提供的測試數(shù)據(jù)https://alibaba.github.io/fastjson2/benchmark_cn可以看得出來,感興趣可以本地實測一下。
到此為止關(guān)于fastjson2的介紹就結(jié)束了,感謝大家的觀看。
我個人也是摸索著去學(xué)習(xí)和閱讀,對于有些解釋可能還存在一些誤區(qū)和誤讀,希望愛好閱讀源碼的朋友們幫忙指點出來。本文僅作為大家閱讀源碼的參考,希望有更多的fastjson2的源碼閱讀類文章出現(xiàn),便于大家一起學(xué)習(xí)。
