一、概述
Fastjson 是阿里巴巴開源的高性能 JSON 序列化處理庫,其主要以處理小數據時速度最快而著稱,功能全面。Fastjson1.X版本目前已停止維護,被Fastjson2.X代替,但1.X版本國內被廣泛使用,通過學習其技術架構,剖析架構上優缺點,對技術人員提升軟件設計工程實踐能力很有價值。
首先我們對“序列化 / 反序列化”概念上建立直觀認識,把Java對象轉化為JSON格式的字符串的過程叫做序列化操作,反之則叫反序列化。如果把“序列化 / 反序列化”放到整個計算機系統的座標系裏,可以把它看成一次數據的“跨邊界搬家”。
對象在“內存世界”裏活得很好,但只要一離開進程地址空間(網絡、磁盤、數據庫、瀏覽器、異構語言),就必須先打成包裹(序列化),到對岸再拆包裹(反序列化)。
二、核心模塊架構
從高層次視圖看Fastjson框架的結構,主要可以分為用户接口層、配置管理層、序列化引擎、反序列化引擎和安全防護層。其中用户接口提供了門面類用户編碼直接與門面類交互,降低使用複雜度;配置管理層允許用户對框架行為進行配置;序列化引擎是序列化操作的核心實現;反序列引擎是反序列化操作的核心實現;安全模塊解決框架安全問題,允許用户針對安全問題設置黑白名單等安全檢查功能。下圖為Fastjson模塊關係圖:
模塊關係圖
三、項目結構
com.alibaba.fastjson/
├── JSON.java # 核心入口類
├── annotation/ # 註解定義
├── asm/ # ASM字節碼精簡庫
├── parser/ # 解析器模塊
│ ├── DefaultJSONParser.java # 默認JSON解析器
│ ├── JSONLexer.java # 詞法分析器接口
│ ├── JSONScanner.java # 詞法分析器實現
│ └── deserializer/ # 反序列化器
├── serializer/ # 序列化器模塊
│ ├── JSONSerializer.java # JSON序列化器
│ ├── SerializeConfig.java # 序列化配置
│ └── ObjectSerializer.java # 對象序列化器接口
├── spi/ # SPI擴展機制
├── support/ # 框架支持
└── util/ # 工具類
3.1 項目結構説明
主要可以劃分為以下幾個核心模塊(包):
com.alibaba.fastjson (核心 API 與數據結構)
- 關鍵類 :
-
- JSON.java: 整個庫的門面(Facade),提供了最常用、最便捷的靜態方法,如 toJSONString() (序列化), parseObject() (反序列化為對象), parseArray() (反序列化為數組)。通常它是用户最先接觸到的類。
- JSONObject.java: 繼承自java.util.HashMap,用於表示 JSON 對象結構( {key: value} )。
- JSONArray.java: 繼承自java.util.ArrayList,用於表示 JSON 數組結構 ( [value1, value2] )。
com.alibaba.fastjson.serializer (序列化模塊)
此模塊負責將 Java 對象轉換為 JSON 格式的字符串
- 關鍵類 :
-
- JSONSerializer.java: 序列化的核心調度器。它維護了序列化的上下文信息,如對象引用、循環依賴檢測、特性( SerializerFeature )開關等,並驅動整個序列化過程。
- SerializeWriter.java: 一個高度優化的 Writer 實現,專門用於生成 JSON 字符串。它內部使用 char[] 數組來拼接字符串,避免了 String 的不可變性帶來的性能損耗,是 Fastjson 高性能寫入的關鍵。
- JavaBeanSerializer.java: 默認的 JavaBean 序列化器。在未啓用 ASM 優化時,它通過反射獲取對象的屬性( getter 方法)並將其序列化。
- ASMSerializerFactory.java: 性能優化的核心 。它使用 ASM 字節碼技術在運行時動態生成序列化器類,這些類直接調用 getter 方法並操作SerializeWriter,避免了反射的性能開銷。
- ObjectSerializer.java: 序列化器接口。用户可以通過實現此接口來為特定類型提供自定義的序列化邏輯。
- SerializeConfig.java: 序列化配置類。它維護了 Java 類型到 ObjectSerializer 的緩存。 SerializeConfig.getGlobalInstance() 提供了全局唯一的配置實例。
- SerializerFeature.java: 序列化特性枚舉。定義了各種序列化行為的開關,例如 WriteMapNullValue (輸出 null 值的字段)、 DisableCircularReferenceDetect (禁用循環引用檢測) 等。
com.alibaba.fastjson.parser (反序列化模塊)
此模塊負責將 JSON 格式的字符串解析為 Java 對象。
- 關鍵類 :
-
- DefaultJSONParser.java: 反序列化的核心調度器。它負責解析 JSON 字符串的整個過程,管理 JSONLexer進行詞法分析,並根據 Token (如 { , } , [ , ] , string , number 等)構建 Java 對象。
- JSONLexer.java / JSONLexerBase.java: JSON 詞法分析器。它負責掃描輸入的 JSON 字符串,將其切割成一個個有意義的 Token ,供 DefaultJSONParser 使用。
- JavaBeanDeserializer.java: 默認的 JavaBean 反序列化器。在未啓用 ASM 優化時,它通過反射創建對象實例並設置其屬性值。
- ASMDeserializerFactory.java: 與序列化類似,它動態生成反序列化器字節碼,直接調用 setter 方法或直接對字段賦值,避免了反射。
- ObjectDeserializer.java: 反序列化器接口。用户可以實現此接口來自定義特定類型的反序列化邏輯。
- ParserConfig.java: 反序列化配置類。維護了 Java 類型到 ObjectDeserializer 緩存,並負責管理 ASM 生成的類的加載。
- Feature.java: 反序列化特性枚舉,用於控制解析行為。
com.alibaba.fastjson.annotation (註解模塊)
提供了一系列註解,允許用户通過聲明式的方式精細地控制序列化和反序列化的行為。
- 關鍵註解 :
-
- @JSONField: 最核心的註解,可用於字段或方法上,用於自定義字段名、格式化、序列化/反序列化順序、是否包含等。
- @JSONType: 可用於類上,用於配置該類的序列化器、反序列化器、特性開關等。
3.2 項目結構小結
Fastjson 框架在架構設計體現了“關注點分離”的原則,將序列化、反序列化、API、工具類等清晰地劃分到不同的模塊中。整個框架具有高度的可擴展性,用户可以通過 ObjectSerializer / ObjectDeserializer接口和豐富的註解來滿足各種複雜的定製化需求。
四、核心源碼分析
為了更直觀説明框架實現原理,本文對部分展示的源代碼進行了刪減,有些使用了偽代碼,如需瞭解更多實現細節請讀者閲讀項目源碼(https://github.com/alibaba/fastjson)
整體上Fastjson通過統一的門面API(JSON.toJSONString/parseObject)調用核心控制器(JSONSerializer/DefaultJSONParser),利用ASM字節碼生成或反射機制,配合SerializeWriter/JSONLexer進行高效的Java對象與JSON字符串間雙向轉換,同時提供配置緩存、循環引用檢測和AutoType安全防護等優化機制。下圖為框架處理數據流:
數據流
4.1 序列化原理介紹
序列化步驟主要包括:序列化器查找→JavaBean字段解析→字段值轉換和JSON字符串構建等過程。下圖為序列化處理時序圖:
序列化時序圖
序列化入口與初始化
使用JSON.toJSONString()入口,將person對象轉換為JSON字符串。
Person person = new Person();
String json = JSON.toJSONString(person);
用户調用toJSONString方法進行對象序列化操作,JSON.java包含了多個toJSONString重載方法,共同完成核心類初始化:SerializeConfig,SerializeWriter,JSONSerializer。
//用户不指定SerializeConfig,默認私有全局配置
public static String toJSONString(Object object, SerializeFilter[] filters,
SerializerFeature... features) {
return toJSONString(object, SerializeConfig.globalInstance, filters, null, DEFAULT_GENERATE_FEATURE, features);
}
public static String toJSONString(Object object,
SerializeConfig config,
SerializeFilter[] filters,
String dateFormat,
int defaultFeatures,
SerializerFeature... features) {
SerializeWriter out = new SerializeWriter((Writer) null, defaultFeatures, features);
try {
JSONSerializer serializer = new JSONSerializer(out);
//省略其他代碼...
serializer.write(object); // 核心序列化調用
return out.toString();
} finally {
out.close();
}
}
序列化控制流程
JSONSerializer.write()核心邏輯
write方法的邏輯比較簡單,首先處理null值,然後根據類型查找序列器(ObjectSerializer),最後將序列化邏輯委派給序列化器處理。
public final void write(Object object) {
//如何序列化對象為null,直接寫入"null"字符串
if (object == null) {
out.writeNull();
return;
}
Class<?> clazz = object.getClass();
ObjectSerializer writer = getObjectWriter(clazz); // 類型識別與序列化器選擇
try {
writer.write(this, object, null, null, 0); // 委託給具體序列化器
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
}
類型識別與序列化器策略
框架採用策略化模式將不同類型序列化邏輯封裝成不同的序列化器:
- 基礎類型 : 使用專門的Codec(如StringCodec、IntegerCodec)
- 集合類型 : 使用ListSerializer、MapSerializer等
- JavaBean : 使用JavaBeanSerializer或ASM動態生成的序列化器
- 枚舉類型 : 使用EnumSerializer
SerializeConfig.getObjectWriter方法負責序列化器查找工作:
public ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
// 第一步:緩存查找
ObjectSerializer writer = get(clazz);
if (writer != null) {
return writer;
}
// 第二步:SPI擴展加載(當前線程類加載器)
try {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
if (!(o instanceof AutowiredObjectSerializer)) {
continue;
}
AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
for (Type forType : autowired.getAutowiredFor()) {
put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
}
writer = get(clazz);
if (writer == null) {
// 第三步:SPI擴展加載(JSON類加載器)
final ClassLoader classLoader = JSON.class.getClassLoader();
if (classLoader != Thread.currentThread().getContextClassLoader()) {
// 重複SPI加載邏輯...
}
}
// 第四步:模塊擴展
for (Module module : modules) {
writer = module.createSerializer(this, clazz);
if (writer != null) {
put(clazz, writer);
return writer;
}
}
// 第五步:內置類型匹配
if (writer == null) {
String className = clazz.getName();
Class<?> superClass;
if (Map.class.isAssignableFrom(clazz)) {
put(clazz, writer = MapSerializer.instance);
} else if (List.class.isAssignableFrom(clazz)) {
put(clazz, writer = ListSerializer.instance);
} else if (Collection.class.isAssignableFrom(clazz)) {
put(clazz, writer = CollectionCodec.instance);
} else if (Date.class.isAssignableFrom(clazz)) {
put(clazz, writer = DateCodec.instance);
} else if (clazz.isEnum()) {
// 枚舉處理邏輯
} else if (clazz.isArray()) {
// 數組處理邏輯
} else {
// 第六步:JavaBean序列化器創建
if (create) {
writer = createJavaBeanSerializer(clazz);
put(clazz, writer);
}
}
}
return writer;
}
JavaBean序列化處理
JavaBeanSerializer的write方法實現了Java對象序列化處理核心邏輯:
方法簽名分析:
protected void write(JSONSerializer serializer, //JSON序列化器,提供序列化上下文和輸出流
Object object, //待序列化的Java對象
Object fieldName, //字段名稱,用於上下文追蹤
Type fieldType, //字段類型信息
int features, //序列化特性標誌位
boolean unwrapped //是否展開包裝,用於嵌套對象處理
) throws IOException
序列化流程概覽:
// 1. 空值檢查和循環引用處理
if (object == null) {
out.writeNull();
return;
}
if (writeReference(serializer, object, features)) {
return;
}
// 2. 字段序列化器選擇
final FieldSerializer[] getters;
if (out.sortField) {
getters = this.sortedGetters;
} else {
getters = this.getters;
}
// 3. 上下文設置和格式判斷
SerialContext parent = serializer.context;
if (!this.beanInfo.beanType.isEnum()) {
serializer.setContext(parent, object, fieldName, this.beanInfo.features, features);
}
// 4.遍歷屬性序列化器,完成屬性序列化
for (int i = 0; i < getters.length; ++i) {
FieldSerializer fieldSerializer = getters[i];
// 獲取屬性值
Object propertyValue = this.processValue(serializer, fieldSerializer.fieldContext, object, fieldInfoName,
propertyValue, features);
// 寫入屬性值
fieldSerializer.writeValue(serializer, propertyValue);
}
循環引用檢測:
JavaBeanSerializerwriteReference 方法執行循環引用檢測,Fastjson使用$ref佔位符處理循環引用問題,防止對象循環引用造成解析查詢棧溢出。
public boolean writeReference(JSONSerializer serializer, Object object, int fieldFeatures) {
SerialContext context = serializer.context;
int mask = SerializerFeature.DisableCircularReferenceDetect.mask;
// 檢查是否禁用循環引用檢測
if (context == null || (context.features & mask) != 0 || (fieldFeatures & mask) != 0) {
return false;
}
// 檢查對象是否已存在於引用表中
if (serializer.references != null && serializer.references.containsKey(object)) {
serializer.writeReference(object); // 寫入引用標記
return true;
}
return false;
}
上下文管理與引用追蹤:
序列化採用DFS(深度優先)算法遍歷對象樹,使用 IdentityHashMap\<Object, SerialContext> references 來追蹤對象引用:
- setContext: 建立序列化上下文,記錄對象層次關係
- containsReference: 檢查對象是否已被序列化
- popContext: 序列化完成後清理上下文
<!---->
protected IdentityHashMap<Object, SerialContext> references = null;
protected SerialContext context;
//使用鏈表建立序列化上下文引用鏈,記錄對象層次關係
public void setContext(SerialContext parent, Object object, Object fieldName, int features, int fieldFeatures) {
if (out.disableCircularReferenceDetect) {
return;
}
//構建當前上下文到parent上下文引用鏈
this.context = new SerialContext(parent, object, fieldName, features, fieldFeatures);
if (references == null) {
references = new IdentityHashMap<Object, SerialContext>();
}
this.references.put(object, context);
}
//檢查對象是否已被序列化,防止重複序列化
public boolean containsReference(Object value) {
if (references == null) {
return false;
}
SerialContext refContext = references.get(value);
if (refContext == null) {
return false;
}
if (value == Collections.emptyMap()) {
return false;
}
Object fieldName = refContext.fieldName;
return fieldName == null || fieldName instanceof Integer || fieldName instanceof String;
}
//清理上下文,將當前序列化上下文指向父親節點
public void popContext() {
if (context != null) {
this.context = this.context.parent;
}
}
字段值轉換與序列化
FieldSerializer.writeValue()核心邏輯
FieldSerializer 的writeValue方法實現了字段值的序列化操作:
public void writeValue(JSONSerializer serializer, Object propertyValue) throws Exception {
// 運行時類型識別
Class<?> runtimeFieldClass = propertyValue != null ?
propertyValue.getClass() : this.fieldInfo.fieldClass;
// 查找屬性類型對應的序列化器
ObjectSerializer fieldSerializer = serializer.getObjectWriter(runtimeFieldClass);
// 處理特殊格式和註解
if (format != null && !(fieldSerializer instanceof DoubleSerializer)) {
serializer.writeWithFormat(propertyValue, format);
return;
}
// 委託給具體序列化器處理
fieldSerializer.write(serializer, propertyValue, fieldInfo.name,
fieldInfo.fieldType, fieldFeatures);
}
不同類型的序列化策略
基礎類型序列化 :
- 直接調用SerializeWriter的對應方法(writeInt、writeString等)
複雜對象序列化 :
- 遞歸調用JSONSerializer.write()方法
- 維護序列化上下文和引用關係
- 應用過濾器和特性配置
ASM定製化序列化器加速,下文會進行詳細講解。
- 為序列化的類動態生成定製化的序列化器,避免反射調用開銷
JSON字符串構建
SerializeWriter.java採用線程本地緩衝機制,提供高效的字符串構建:
//用於存儲存JSON字符串
private final static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
//將字符串轉換為UTF-8字節數組
private final static ThreadLocal<byte[]> bytesBufLocal = new ThreadLocal<byte[]>();
- 字符緩衝區 : 線程本地char[]數組減少內存分配,避免頻繁創建臨時數組對象。
- 動態擴容 : 根據內容長度自動調整緩衝區大小。
bufLocal初始化創建2048字符的緩衝區,回收階段當緩衝區大小不超過 BUFFER\_THRESHOLD (128KB)時,將其放回ThreadLocal緩存,超過閾值的大緩衝區不緩存,避免內存佔用過大。
bytesBufLocal專門用於UTF-8編碼轉換過程,初始緩衝區大小:8KB(1024 * 8),根據字符數量估算所需字節數(字符數 × 3),只有不超過 BUFFER\_THRESHOLD 的緩衝區才會被緩存。
4.2 序列化小結
Fastjson通過JSON.toJSONString()門面API調用JSONSerializer控制器,利用ASM字節碼生成的高性能序列化器或反射機制遍歷Java對象字段,配合SerializeWriter將字段名和值逐步寫入緩衝區構建JSON字符串。
4.3 反序列化流程
雖然“序列化”與“反序列化”在概念上是對偶的(Serialize ↔ Deserialize),但在實現層面並不嚴格對偶,反序列化實現明顯比序列化複雜。核心步驟包括:反序列化器查找→ 反序列流程控制→詞法分析器(Tokenizer) → 安全檢查→反射/ASM 字段填充等,下圖為處理時序圖:
反序列化入口與反序列化器選擇
反序列化從 JSON.java的parseObject方法開始:
// JSON.java - 反序列化入口
public static <T> T parseObject(String text, Class<T> clazz, int features) {
if (text == null) {
return null;
}
DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
T value = (T) parser.parseObject(clazz);
parser.handleResovleTask(value);
parser.close();
return value;
}
查找反序列化器
在 DefaultJSONParser.java 中選擇合適的反序列化器:
// DefaultJSONParser.java - 反序列化器選擇
public <T> T parseObject(Type type, Object fieldName) {
int token = lexer.token();
if (token == JSONToken.NULL) {
lexer.nextToken();
return (T) TypeUtils.optionalEmpty(type);
}
//從緩存中查找反序列化器
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
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.java 負責獲取對應類型的反序列化器:
// ParserConfig.java - 反序列化器獲取
public ObjectDeserializer getDeserializer(Type type) {
ObjectDeserializer deserializer = this.deserializers.get(type);
if (deserializer != null) {
return deserializer;
}
//通過Class查找
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);
}
}
return JavaObjectDeserializer.instance;
}
反序列化控制流程
JavaBeanDeserializer.java 的deserialze實現了反序列化主要處理流程。
// JavaBeanDeserializer.java - 類型識別與字段匹配
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, int features, int[] setFlags) {
// 1.特殊類型快速處理
if (type == JSON.class || type == JSONObject.class) {
return (T) parser.parse();
}
//2.初始化核心組件
final JSONLexer lexer = parser.lexer;
//3.反序列化上下文管理
ParseContext context = parser.getContext();
if (object != null && context != null) {
context = context.parent;
}
ParseContext childContext = null;
//保存解析後字段值
Map<String, Object> fieldValues = null;
// JSON關鍵字分支預處理
if (token == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
if (object == null) {
object = createInstance(parser, type);
}
return (T) object;
}
//處理其他JSON關鍵字
...
//4.字段解析主循環
for (int fieldIndex = 0, notMatchCount = 0;; fieldIndex++) {
boolean customDeserializer = false;
//這是一個性能優化的設計,通過預排序和索引訪問來提高字段匹配的效率,
//通常情況下JSON串按字段定義順序排列,因此能快速命中
if (fieldIndex < sortedFieldDeserializers.length && notMatchCount < 16) {
fieldDeserializer = sortedFieldDeserializers[fieldIndex];
fieldInfo = fieldDeserializer.fieldInfo;
fieldClass = fieldInfo.fieldClass;
fieldAnnotation = fieldInfo.getAnnotation();
if (fieldAnnotation != null && fieldDeserializer instanceof DefaultFieldDeserializer) {
customDeserializer = ((DefaultFieldDeserializer) fieldDeserializer).customDeserilizer;
}
}
Object fieldValue = null;
if (fieldDeserializer != null) {
char[] name_chars = fieldInfo.name_chars;
//指定了自定義發序列化器,後續使用自定義序列化器處理
if (customDeserializer && lexer.matchField(name_chars)) {
matchField = true;
// 基本類型快速路徑匹配
} else if (fieldClass == int.class || fieldClass == Integer.class) {
//詞法分析,解析int值
int intVal = lexer.scanFieldInt(name_chars);
if (intVal == 0 && lexer.matchStat == JSONLexer.VALUE_NULL) {
fieldValue = null;
} else {
fieldValue = intVal;
}
if (lexer.matchStat > 0) {
matchField = true;
valueParsed = true;
} else if (lexer.matchStat == JSONLexer.NOT_MATCH_NAME) {
//增加計算,記錄未命中次數以調整匹配策略
notMatchCount++;
continue;
}
} else if(...){
//省略其他基礎類型處理
}
}
// 快速匹配失敗,動態掃描字段名,通過符號表優化:返回的字符串可能是符號表中的緩存實例
if (!matchField) {
key = lexer.scanSymbol(parser.symbolTable);
// $ref 引用處理
if ("$ref" == key && context != null) {
handleReferenceResolution(lexer, parser, context)
}
// @type 類型處理
if ((typeKey != null && typeKey.equals(key))
|| JSON.DEFAULT_TYPE_KEY == key) {
//AutoType安全檢查
config.checkAutoType(typeName, expectClass, lexer.getFeatures());
handleTypeNameResolution(lexer, parser, config, beanInfo, type, fieldName);
}
}
}
// 5.如果對象為空,則創建對象實例
if (object == null && fieldInfo == null) {
object = createInstance(parser, type);
if (object == null) {
return null;
}
}
//6. 字段值設置
for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
FieldDeserializer fieldDeserializer = getFieldDeserializer(entry.getKey());
if (fieldDeserializer != null) {
fieldDeserializer.setValue(object, entry.getValue());
}
}
return (T) object;
}
字符串解析階段(詞法分析)
JSONLexerBase內部維護詞法解析狀態機,實現詞法分析核心邏輯,下面展示了Integer值類型處理源碼:
public int scanFieldInt(char[] fieldName) {
matchStat = UNKNOWN;
// 1. 字段名匹配階段
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
// 2. 負號處理
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(bp + (offset++));
}
// 3. 數字解析核心算法
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');// 十進制累加
} else if (chLocal == '.') {
matchStat = NOT_MATCH; // 拒絕浮點數
return 0;
} else {
break;
}
}
// 4. 溢出檢測
if (value < 0 //
|| offset > 11 + 3 + fieldName.length) {
if (value != Integer.MIN_VALUE //
|| offset != 17 //
|| !negative) {
matchStat = NOT_MATCH;
return 0;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
// 5. JSON 結束符處理
if (chLocal == ',') {
bp += offset;
this.ch = this.charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
}
if (chLocal == '}') {
// ... 處理對象結束和嵌套結構
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += offset;
this.ch = this.charAt(bp);
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return negative ? -value : value;
}
類型安全檢查(AutoType檢查)
ParserConfig.java 中的checkAutoType方法對反序列化類型做黑白名單檢查。
// ParserConfig.java - AutoType安全檢查
public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
if (typeName == null) {
return null;
}
if (typeName.length() >= 192 || typeName.length() < 3) {
throw new JSONException("autoType is not support. " + typeName);
}
String className = typeName.replace('$', '.');
Class<?> clazz = null;
final long BASIC = 0xcbf29ce484222325L;
final long PRIME = 0x100000001b3L;
final long h1 = (BASIC ^ className.charAt(0)) * PRIME;
// hash code編碼匹配性能優化
if (h1 == 0xaf64164c86024f1aL) {
throw new JSONException("autoType is not support. " + typeName);
}
if ((h1 ^ className.charAt(className.length() - 1)) * PRIME == 0x9198507b5af98f0L) {
throw new JSONException("autoType is not support. " + typeName);
}
final long h3 = (((((BASIC ^ className.charAt(0))
* PRIME)
^ className.charAt(1))
* PRIME)
^ className.charAt(2))
* PRIME;
if (autoTypeSupport || expectClass != null) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= PRIME;
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
throw new JSONException("autoType is not support. " + typeName);
}
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
if (clazz != null) {
return clazz;
}
}
}
}
// ... 更多安全檢查邏輯
return clazz;
}
對象實例化過程
JavaBeanDeserializer.java中的createInstance方法創建對象實例:
// JavaBeanDeserializer.java - 對象實例化
protected Object createInstance(DefaultJSONParser parser, Type type) {
if (type instanceof Class) {
if (clazz.isInterface()) {
// 接口類型使用Java反射創建實例
Class<?> clazz = (Class<?>) type;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
final JSONObject obj = new JSONObject();
Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { clazz }, obj);
return proxy;
}
}
if (beanInfo.defaultConstructor == null && beanInfo.factoryMethod == null) {
return null;
}
Object object;
try {
//通過構造器創建實例
Constructor<?> constructor = beanInfo.defaultConstructor;
if (beanInfo.defaultConstructorParameterSize == 0) {
object = constructor.newInstance();
} else {
ParseContext context = parser.getContext();
if (context == null || context.object == null) {
throw new JSONException("can't create non-static inner class instance.");
}
final Class<?> enclosingClass = constructor.getDeclaringClass().getEnclosingClass();
object = constructor.newInstance(context.object);
}
} catch (JSONException e) {
throw e;
} catch (Exception e) {
throw new JSONException("create instance error, class " + clazz.getName(), e);
}
return object;
}
FieldDeserializer.java中的setValue方法通過反射實現字段設置:
// FieldDeserializer.java - 屬性賦值的核心實現
public void setValue(Object object, Object value) {
if (value == null && fieldInfo.fieldClass.isPrimitive()) {
return;
} else if (fieldInfo.fieldClass == String.class
&& fieldInfo.format != null
&& fieldInfo.format.equals("trim")) {
value = ((String) value).trim();
}
try {
Method method = fieldInfo.method;
if (method != null) {
if (fieldInfo.getOnly) {
// 處理只讀屬性的特殊情況
if (fieldInfo.fieldClass == AtomicInteger.class) {
AtomicInteger atomic = (AtomicInteger) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicInteger) value).get());
}
} else if (Map.class.isAssignableFrom(method.getReturnType())) {
Map map = (Map) method.invoke(object);
if (map != null) {
map.putAll((Map) value);
}
} else {
Collection collection = (Collection) method.invoke(object);
if (collection != null && value != null) {
collection.clear();
collection.addAll((Collection) value);
}
}
} else {
// 通過setter方法賦值
method.invoke(object, value);
}
} else {
// 通過字段直接賦值
final Field field = fieldInfo.field;
if (field != null) {
field.set(object, value);
}
}
} catch (Exception e) {
throw new JSONException("set property error, " + clazz.getName() + "#" + fieldInfo.name, e);
}
}
4.4 反序列化小結
Fastjson通過JSON.parseObject()門面API調用DefaultJSONParser控制器,利用JSONLexer進行詞法分析解析JSON字符串,經過AutoType安全檢查後使用ASM字節碼生成動態反序列化器或反射機制創建Java對象實例並逐字段賦值。
五、特性講解
5.1 ASM性能優化
ASM 是 fastjson 類似於 JIT,在運行時把「反射調用」翻譯成「直接字段訪問 + 方法調用」的字節碼,從而把序列化/反序列化性能提升 20% 以上,當然隨着JVM對反射性能的優化性能差正在逐漸被縮小。下圖是作者使用工具類讀取的動態序列化/反序列化器源碼片段。
5.2 AutoType機制
AutoType是 fastjson 的“動態多態還原”方案:
序列化時把具體子類名字寫進 "@type",反序列化時先加載類 → 再調 setter → 完成還原。
速度上“指針引用”即可定位序列化器,功能上靠 @type 字段把被擦除的泛型/接口/父類重新映射回具體實現。
在未開啓AutoType機制情況下,在將store對象序列化成JSON串後,再反序列化為對象時由於字段的類型為接口無法轉換成具體的Dog類型示例;開啓AutoType機制後,序列化時將類型一併寫入到JSON串內,後續進行反序列化時可以根據這個類型還原成具體的類型實例。
interface Animal {}
class Dog implements Animal {
private String name;
private double weight;
//省略getter,setter
}
class PetStore {
private Animal animal;
}
public static void main(String[] args) {
Animal dog = new Dog("dodi", 12);
PetStore store = new PetStore(dog);
String jsonString = JSON.toJSONString(store);
PetStore petStore = JSON.parseObject(jsonString, PetStore.class);
Dog parsedDog = (Dog) petStore.getAnimal();
}
public static void main(String[] args) {
Animal dog = new Dog("dodi", 12);
PetStore store = new PetStore(dog);
String jsonString = JSON.toJSONString(store, SerializerFeature.WriteClassName);
PetStore petStore = JSON.parseObject(jsonString, PetStore.class);
Dog parsedDog = (Dog) petStore.getAnimal();
}
AutoType 讓 fastjson 在反序列化時根據 @type 字段動態加載任意類,這一“便利”卻成為攻擊者遠程代碼執行的快捷通道:通過把JdbcRowSetImpl等 JNDI 敏感類寫進 JSON,服務端在調用 setter 的瞬間就會向外部 LDAP/RMI 服務器拉取惡意字節碼,完成 RCE;而官方長期依賴“黑名單”堵漏,導致 1.2.25→1.2.80 出現 L 描述符、Throwable 二次反序列化、內部類等連續繞過,形成“補丁-繞過-再補丁”的貓鼠遊戲, 雖然在1.2.68 引入 safeMode 但為了兼容性需要使用者手動開啓 ,而且實現也不夠健壯,開啓safeMode仍有利用代碼漏洞繞過檢查風險,後續版本對safeMode加固並對已知安全漏洞清零,直到最新1.2.83版本安全問題也不能説徹底解決。
5.3 流式解析
Fastjson 提供一套 Streaming API,核心類JSONReader /JSONWriter,行業內慣稱「流式解析」或「增量解析」,主要用於處理JSON大文件解析。技術上流式解析採用“拉模式(pull parsing)”,底層維護 8 KB 滑動緩衝,詞法分析器(Tokenizer)把字節流切成 token 流,語法狀態機根據 token 類型驅動反序列化器(ObjectReader)即時產出 Java 對象,對象一旦交付給用户代碼處理後,內部引用立即釋放。這種方式內存中不會保存所有對象,對象處理完即被丟棄,因此可以處理數據量遠大於內存的數據,而不會出現OOM。下面是使用流式解析的示例代碼:
// 依賴:com.alibaba:fastjson:1.2.83
try (JSONReader reader = new JSONReader(
new InputStreamReader(
new FileInputStream("huge-array.json"), StandardCharsets.UTF_8))) {
reader.startArray(); // 告訴解析器:根節點是 []
while (reader.hasNext()) { // 拉取下一條
Order order = reader.readObject(Order.class); // 瞬時對象
processOrder(order);//業務處理
orderRepository.save(order); // 立即落盤,內存即可回收
}
reader.endArray();
}
六、總結
Fastjson核心特性在於高速序列化/反序列化,利用ASM在運行時生成字節碼動態創建解析器,減少反射;AutoType字段支持多態,卻帶來反序列化RCE風險,建議關閉AutoType,開啓safeMode。選型建議:在選擇JSON序列化框架時對於非極端性能要求推薦Jackson,或者使用Fastjson2,其改用LambdaMetafactory替換ASM,性能再提升30%,默認關閉AutoType安全性有保證。
參考資料:
- FastJson 反序列化漏洞原理分析(https://www.cnblogs.com/Only-xiaoxiao/p/17213248.html)
- 序列化與反序列化——FastJSON、Jackson、Gson性能測試(https://zhuanlan.zhihu.com/p/529342385)
- FASTJSON 2 Autotype機制介紹(https://alibaba.github.io/fastjson2/autotype_cn.html)
*
往期回顧
1. 用好 TTL Agent 不踩雷:避開內存泄露與CPU 100%兩大核心坑|得物技術
2. 線程池ThreadPoolExecutor源碼深度解析|得物技術
3. 基於瀏覽器擴展 API Mock 工具開發探索|得物技術
4. 破解gh-ost變更導致MySQL表膨脹之謎|得物技術
5. MySQL單表為何別超2000萬行?揭秘B+樹與16KB頁的生死博弈|得物技術
文 /劍九
關注得物技術,每週更新技術乾貨
要是覺得文章對你有幫助的話,歡迎評論轉發點贊~
未經得物技術許可嚴禁轉載,否則依法追究法律責任。