最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Fastjson反序列化隨機性失敗示例詳解

 更新時間:2022年08月03日 11:22:19   作者:阿里巴巴大淘寶技術(shù)  
這篇文章主要為大家介紹了Fastjson反序列化隨機性失敗示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

本文主要講述了一個具有"隨機性"的反序列化錯誤!

Fastjson作為一款高性能的JSON序列化框架,使用場景眾多,不過也存在一些潛在的bug和不足。本文主要講述了一個具有"隨機性"的反序列化錯誤!

問題代碼

為了清晰地描述整個報錯的來龍去脈,將相關(guān)代碼貼出來,同時也為了可以本地執(zhí)行,看一下實際效果。

StewardTipItem

package test;
import java.util.List;
public class StewardTipItem {
    private Integer type;
    private List<String> contents;
    public StewardTipItem(Integer type, List<String> contents) {
        this.type = type;
        this.contents = contents;
    }
}

StewardTipCategory

反序列化時失敗,此類有兩個特殊之處:

  • 返回StewardTipCategory的build方法(忽略返回null值)。
  • 構(gòu)造函數(shù)『C1』Map<Integer, List> items參數(shù)與List items屬性同名,但類型不同!
package test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class StewardTipCategory {
    private String category;
    private List<StewardTipItem> items;
    public StewardTipCategory build() {
        return null;
    }
    //C1 下文使用C1引用該構(gòu)造函數(shù)
    public StewardTipCategory(String category, Map<Integer,List<String>> items) {          
        List<StewardTipItem> categoryItems = new ArrayList<>();
    for (Map.Entry<Integer, List<String>> item : items.entrySet()) { 
        StewardTipItem tipItem = new StewardTipItem(item.getKey(), item.getValue());                   categoryItems.add(tipItem);
    }
    this.items = categoryItems;
    this.category = category; 
}
    // C2 下文使用C2引用該構(gòu)造函數(shù)
    public StewardTipCategory(String category, List<StewardTipItem> items) {        
        this.category = category;
        this.items = items;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public List<StewardTipItem> getItems() {
        return items; 
    }
    public void setItems(List<StewardTipItem> items) {
        this.items = items; 
    }
}

StewardTip

package test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class StewardTip {
    private List<StewardTipCategory> categories;
    public StewardTip(Map<String, Map<Integer, List<String>>> categories) {          
        List<StewardTipCategory> tipCategories = new ArrayList<>();
        for (Map.Entry<String, Map<Integer, List<String>>> category : categories.entrySet()) {             StewardTipCategory tipCategory = new StewardTipCategory(category.getKey(), category.getValue());
            tipCategories.add(tipCategory);
        }
        this.categories = tipCategories; 
    }
    public StewardTip(List<StewardTipCategory> categories) {
        this.categories = categories;
    }
    public List<StewardTipCategory> getCategories() { 
        return categories;
    }
    public void setCategories(List<StewardTipCategory> categories) {
        this.categories = categories;
    }
}

JSON字符串

{
    "categories":[
        {
             "category":"工藝類",
             "items":[
                 {
                     "contents":[
                         "工藝類-提醒項-內(nèi)容1", 
                         "工藝類-提醒項-內(nèi)容2"
                     ],
                     "type":1
                }, 
                { 
                     "contents":[ 
                         "工藝類-疑問項-內(nèi)容1" 
                     ], 
                     "type":2
                }
            ]
        }
    ]
}

FastJSONTest

package test;
import com.alibaba.fastjson.JSONObject;
public class FastJSONTest {
    public static void main(String[] args) {
        String tip = "{"categories":[{"category":"工藝類","items":[{"contents":["工藝類-提醒項-內(nèi)容1","工藝類-提醒項-內(nèi)容2"],"type":1},{"contents":["工藝類-疑問項-內(nèi)容1"],"type":2}]}]}";        
        try {
            JSONObject.parseObject(tip, StewardTip.class); 
        } catch (Exception e) {
            e.printStackTrace(); 
        }
    }
}

堆棧信息

當執(zhí)行FastJSONTest的main方法時報錯:

com.alibaba.fastjson.JSONException: syntax error, expect {, actual [
    at com.alibaba.fastjson.parser.deserializer.MapDeserializer.parseMap(MapDeserializer.java:228)
    at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:67)  
    at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:43)  
    at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:85)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:838)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:284)
    at com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer.parseArray(ArrayListTypeFieldDeserializer.java:181)
    at com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer.parseField(ArrayListTypeFieldDeserializer.java:69) 
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:838)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:288)
    at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:672)  at com.alibaba.fastjson.JSON.parseObject(JSON.java:396) 
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
    at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
    at test.FastJSONTest.main(FastJSONTest.java:17)

問題排查

排查過程有兩個難點:

  • 不能根據(jù)報錯信息得到異常時JSON字符串的key,position或者其他有價值的提示信息。
  • 報錯并不是每次執(zhí)行都會發(fā)生,存在隨機性,執(zhí)行十次可能報錯兩三次,沒有統(tǒng)計失敗率。

經(jīng)過多次執(zhí)行之后還是找到了一些蛛絲馬跡!下面結(jié)合源碼對整個過程進行簡單地敘述,最后也會給出怎么能在報錯的時候debug到代碼的方法。

JavaBeanInfo:285行

clazz是StewardTipCategory.class的情況下,提出以下兩個問題:

Q1:Constructor[] constructors數(shù)組的返回值是什么?

Q2:constructors數(shù)組元素的順序是什么?

參考java.lang.Class#getDeclaredConstructors的注釋,可得到A1:

  • A1

public test.StewardTipCategory(java.lang.String,java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>)『C1』

public test.StewardTipCategory(java.lang.String,java.util.List<test.StewardTipItem>)『C2』

  • A2

build()方法,C1構(gòu)造函數(shù),C2構(gòu)造函數(shù)三者在Java源文件的順序決定了constructors數(shù)組元素的順序!

?下表是經(jīng)過多次實驗得到的一組數(shù)據(jù),因為是手動觸發(fā),并且次數(shù)較少,所以不能保證100%的準確性,只是一種大概率事件。

java.lang.Class#getDeclaredConstructors底層實現(xiàn)是native getDeclaredConstructors0,JVM的這部分代碼沒有去閱讀,所以目前無法解釋產(chǎn)生這種現(xiàn)象的原因。

數(shù)組元素順序
build()C1C2隨機
C1build()C2C2,C1
C1C2build()C2,C1
build()C2C1隨機
C2build()C1C1,C2
C2C1build()C1,C2
C1 C2C2,C1
C2C1C1,C2 

正是因為java.lang.Class#getDeclaredConstructors返回數(shù)組元素順序的隨機性,才導(dǎo)致反序列化失敗的隨機性!

  • [C2,C1]反序列化成功!
  • [C1,C2]反序列化失??!

[C1,C2]順序下探尋反序列化失敗時代碼執(zhí)行的路徑。

JavaBeanInfo:492行

com.alibaba.fastjson.util.JavaBeanInfo#build()方法體代碼量比較大,忽略執(zhí)行路徑上的無關(guān)代碼。\

  • [C1,C2]順序下代碼會執(zhí)行到492行,并執(zhí)行兩次(StewardTipCategory#category, StewardTipCategory#items各執(zhí)行一次)。
  • 結(jié)束后創(chuàng)建一個com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer。

JavaBeanDeserializer:49行

JavaBeanDeserializer兩個重要屬性:

private final FieldDeserializer[]   fieldDeserializers;

protected final FieldDeserializer[] sortedFieldDeserializers;

反序列化test.StewardTipCategory#items時fieldDeserializers的詳細信息。

com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializercom.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer#fieldValueDeserilizer(屬性值null,運行時會根據(jù)fieldType獲取具體實現(xiàn)類)com.alibaba.fastjson.util.FieldInfo#fieldType(java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>)

創(chuàng)建完成執(zhí)行

com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#deserialze(com.alibaba.fastjson.parser.DefaultJSONParser, java.lang.reflect.Type, java.lang.Object, java.lang.Object, int, int[])

JavaBeanDeserializer:838行

DefaultFieldDeserializer:53行

com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type)根據(jù)字段類型設(shè)置

com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer#fieldValueDeserilizer的具體實現(xiàn)類。

DefaultFieldDeserializer:34行

test.StewardTipCategory#items屬性的實際類型是List。

反序列化時根據(jù)C1構(gòu)造函數(shù)得到的fieldValueDeserilizer的實現(xiàn)類是com.alibaba.fastjson.parser.deserializer.MapDeserializer。

執(zhí)行

com.alibaba.fastjson.parser.deserializer.MapDeserializer#deserialze(com.alibaba.fastjson.parser.DefaultJSONParser, java.lang.reflect.Type, java.lang.Object)時報錯。

MapDeserializer:228行

JavaBeanDeserializer:838行

java.lang.Class#getDeclaredConstructors返回[C2,C1]順序,反序列化時根據(jù)C2構(gòu)造函數(shù)得到的fieldValueDeserilizer的實現(xiàn)類是

com.alibaba.fastjson.parser.deserializer.ArrayListTypeFieldDeserializer,反序列化成功。

問題解決

代碼

  • 刪除C1構(gòu)造函數(shù),使用其他方式創(chuàng)建StewardTipCategory。
  • 修改C1構(gòu)造函數(shù)參數(shù)名稱,類型,避免誤導(dǎo)Fastjson。

調(diào)試

package test;
import com.alibaba.fastjson.JSONObject;
import java.lang.reflect.Constructor;
public class FastJSONTest {
    public static void main(String[] args) { 
        Constructor<?>[] declaredConstructors = StewardTipCategory.class.getDeclaredConstructors(); 
        // if true must fail!
       if ("public test.StewardTipCategory(java.lang.String,java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>)".equals(declaredConstructors[0].toGenericString())) {                 
           String tip = "{"categories":[{"category":"工藝類","items":[{"contents":["工藝類-提醒項-內(nèi)容1","工藝類-提醒項-內(nèi)容2"],"type":1},{"contents":["工藝類-疑問項-內(nèi)容1"],"type":2}]}]}";                   
           try { 
                JSONObject.parseObject(tip, StewardTip.class);
            } catch (Exception e) {  
                e.printStackTrace();
            }
        }
    }
}

總結(jié)

開發(fā)過程中盡量遵照規(guī)范/規(guī)約,不要特立獨行

StewardTipCategory構(gòu)造函數(shù)C1方法簽名明顯不是一個很好的選擇,方法體除了屬性賦值,還做了一些額外的類型/數(shù)據(jù)轉(zhuǎn)換,也應(yīng)該盡量避免。

專業(yè)有深度

開發(fā)人員對于使用的技術(shù)與框架要有深入的研究,尤其是底層原理,不能停留在使用層面。一些不起眼的事情可能導(dǎo)致不可思議的問題:java.lang.Class#getDeclaredConstructors。

Fastjson

框架實現(xiàn)時要保持嚴謹,報錯信息盡可能清晰明了,StewardTipCategory反序列化失敗的原因在于,fastjson只檢驗了屬性名稱,構(gòu)造函數(shù)參數(shù)個數(shù)而沒有進一步校驗屬性類型。

<<重構(gòu):改善既有代碼的設(shè)計>>提倡代碼方法塊盡量短小精悍,F(xiàn)astjson某些模塊的方法過于臃腫。

以上就是Fastjson反序列化隨機性失敗示例詳解的詳細內(nèi)容,更多關(guān)于Fastjson反序列化隨機性的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring Boot + Thymeleaf + Activiti 快速開發(fā)平臺項目 附源碼

    Spring Boot + Thymeleaf + Activiti 快速開發(fā)平臺項目 附源碼

    這篇文章主要介紹了Spring Boot + Thymeleaf + Activiti 快速開發(fā)平臺項目附源碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • 將Java程序與數(shù)據(jù)庫進行連接的操作方法

    將Java程序與數(shù)據(jù)庫進行連接的操作方法

    這篇文章主要介紹了將Java程序與數(shù)據(jù)庫進行連接的操作方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • java8傳函數(shù)方法圖文詳解

    java8傳函數(shù)方法圖文詳解

    在本篇文章中小編給大家整理了關(guān)于java8傳函數(shù)方法和相關(guān)知識點,需要的朋友們學(xué)習(xí)下。
    2019-04-04
  • java大話之創(chuàng)建型設(shè)計模式教程示例

    java大話之創(chuàng)建型設(shè)計模式教程示例

    這篇文章主要為大家介紹了java大話之創(chuàng)建型設(shè)計模式教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • Java實例項目零錢通的實現(xiàn)流程

    Java實例項目零錢通的實現(xiàn)流程

    本篇文章為你帶來Java的一個新手實戰(zhàn)項目,是一個零錢通系統(tǒng),項目來自于B站韓順平老師,非常適合新手入門練習(xí),感興趣的朋友快來看看吧
    2022-03-03
  • Java中的HashSet集合解析

    Java中的HashSet集合解析

    這篇文章主要介紹了Java中的HashSet集合解析,HashSet 實現(xiàn) Set 接口,內(nèi)部維護一個 HashMap 實例,它不能保證集合迭代的順序,也不能保證順序不變,HashSet 允許 null 元素,需要的朋友可以參考下
    2023-11-11
  • Java實現(xiàn)添加頁碼到PDF文檔

    Java實現(xiàn)添加頁碼到PDF文檔

    頁碼可以清楚了解總頁數(shù)、定位頁數(shù)快速尋找自己所要的文段、打印時不會分不清頭中尾。今天這篇文章就將介紹如何通過Java代碼,以編程的方式將添加頁碼到PDF文檔,需要的可以參考一下
    2023-04-04
  • Springboot?多租戶SaaS搭建方案

    Springboot?多租戶SaaS搭建方案

    這篇文章主要介紹了Springboot?多租戶SaaS方案,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • java根據(jù)當前時間獲取yyyy-MM-dd?HH:mm:ss標準格式的時間代碼示例

    java根據(jù)當前時間獲取yyyy-MM-dd?HH:mm:ss標準格式的時間代碼示例

    在Java中可以使用java.time包中的LocalDateTime類和DateTimeFormatter類來獲取并格式化當前時間為yyyy-MM-dd?HH:mm:ss的格式,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-10-10
  • Java父線程(或是主線程)等待所有子線程退出的實例

    Java父線程(或是主線程)等待所有子線程退出的實例

    下面小編就為大家分享一篇Java父線程(或是主線程)等待所有子線程退出的實例,具有很好的參考價值,希望對大家有所幫助
    2017-11-11

最新評論

炎陵县| 青川县| 鄄城县| 彭泽县| 呼图壁县| 美姑县| 长武县| 惠水县| 合水县| 柞水县| 牟定县| 壤塘县| 太谷县| 资阳市| 平陆县| 淮滨县| 宁晋县| 社旗县| 深泽县| 舞钢市| 社会| 灌南县| 偏关县| 萍乡市| 达尔| 虹口区| 新乡县| 开江县| 那坡县| 大同市| 清苑县| 通榆县| 新闻| 林周县| 九龙坡区| 奎屯市| 涪陵区| 虎林市| 永川市| 株洲县| 巧家县|