动态

详情 返回 返回

從 JSON 字符串到 Java 對象:Fastjson 1.2.83 全程解析|得物技術 - 动态 详情

一、概述

Fastjson 是阿里巴巴開源的高性能 JSON 序列化處理庫,其主要以處理小數據時速度最快而著稱,功能全面。Fastjson1.X版本目前已停止維護,被Fastjson2.X代替,但1.X版本國內被廣泛使用,通過學習其技術架構,剖析架構上優缺點,對技術人員提升軟件設計工程實踐能力很有價值。

首先我們對“序列化 / 反序列化”概念上建立直觀認識,把Java對象轉化為JSON格式的字符串的過程叫做序列化操作,反之則叫反序列化。如果把“序列化 / 反序列化”放到整個計算機系統的座標系裏,可以把它看成一次數據的“跨邊界搬家”。

對象在“內存世界”裏活得很好,但只要一離開進程地址空間(網絡、磁盤、數據庫、瀏覽器、異構語言),就必須先打成包裹(序列化),到對岸再拆包裹(反序列化)。

image.png

二、核心模塊架構

從高層次視圖看Fastjson框架的結構,主要可以分為用户接口層、配置管理層、序列化引擎、反序列化引擎和安全防護層。其中用户接口提供了門面類用户編碼直接與門面類交互,降低使用複雜度;配置管理層允許用户對框架行為進行配置;序列化引擎是序列化操作的核心實現;反序列引擎是反序列化操作的核心實現;安全模塊解決框架安全問題,允許用户針對安全問題設置黑白名單等安全檢查功能。下圖為Fastjson模塊關係圖:

image.png

模塊關係圖

三、項目結構

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安全防護等優化機制。下圖為框架處理數據流:

image.png

數據流

4.1 序列化原理介紹

序列化步驟主要包括:序列化器查找→JavaBean字段解析→字段值轉換和JSON字符串構建等過程。下圖為序列化處理時序圖:

image.png

序列化時序圖

序列化入口與初始化

使用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 字段填充等,下圖為處理時序圖:

image.png

反序列化入口與反序列化器選擇

反序列化從 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對反射性能的優化性能差正在逐漸被縮小。下圖是作者使用工具類讀取的動態序列化/反序列化器源碼片段。

image.png

image.png

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();
}

image.png

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();
}

image.png

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安全性有保證。

參考資料:

往期回顧

1. 用好 TTL Agent 不踩雷:避開內存泄露與CPU 100%兩大核心坑|得物技術

2. 線程池ThreadPoolExecutor源碼深度解析|得物技術

3. 基於瀏覽器擴展 API Mock 工具開發探索|得物技術

4. 破解gh-ost變更導致MySQL表膨脹之謎|得物技術

5. MySQL單表為何別超2000萬行?揭秘B+樹與16KB頁的生死博弈|得物技術

文 /劍九

關注得物技術,每週更新技術乾貨

要是覺得文章對你有幫助的話,歡迎評論轉發點贊~

未經得物技術許可嚴禁轉載,否則依法追究法律責任。

user avatar xiaoniuhululu 头像 dirackeeko 头像 u_15591470 头像 ligaai 头像 DingyLand 头像 yulong1992 头像 esunr 头像 dalideshoushudao 头像 lyflexi 头像 fabarta 头像 opentiny 头像 old_it 头像
点赞 28 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.