<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          號(hào)稱引領(lǐng)下一個(gè)十年的高性能JSON庫fastjson2登場了

          共 27080字,需瀏覽 55分鐘

           ·

          2022-07-10 17:39

          往期熱門文章:

          1、Lombok同時(shí)使?@Data和@Builder 的坑
          2、IntelliJ IDEA快捷鍵大全 + 動(dòng)圖演示,建議收藏!
          3、如何防止你的 jar 被反編譯?
          4、大公司為什么禁止SpringBoot項(xiàng)目使用Tomcat?
          5、Java 8 的 Stream 不好調(diào)試?別逗了!IDEA 調(diào)試就能輕松搞定?。?/a>

          juejin.cn/post/7115219049931341854


          概述

          FastJson2FastJson項(xiàng)目的重要升級(jí),目標(biāo)是為下一個(gè)十年提供一個(gè)高性能的JSON庫。根據(jù)官方給出的性能來看,相比v1版本,確實(shí)有了很大的提升,本篇文章我們來看下究竟做了哪些事情,使得性能有了大幅度的提升。
          本篇將采用代碼測試 + 源碼閱讀的方式對(duì)FastJson2的性能提升做一個(gè)較為全面的探索。

          一、環(huán)境準(zhǔn)備

          首先,我們搭建一套用于測試的環(huán)境,這里采用 Springboot 項(xiàng)目,分別創(chuàng)建兩個(gè) module:fastjsonfastjson2。使用兩個(gè)版本進(jìn)行對(duì)比試驗(yàn)。
          代碼結(jié)構(gòu)如下所示:
          fastjson2 VS fastjson

          1.1 引入對(duì)應(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方法進(jìn)行測試。
          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;
              }
          }
          /**
          * 定義循環(huán)次數(shù)
          */

          private final static Integer NUM = 100;

          public static void main(String[] args) {
             // 總時(shí)間
             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號(hào)").build());
             }
             // 按指定次數(shù)循環(huán)
             for (int i = 0; i < NUM; i++) {
                 // 單次循環(huán)開始時(shí)間
                 long startTime = System.currentTimeMillis();
                 // 遍歷學(xué)生數(shù)據(jù)
                 studentList.forEach(student -> {
                     // 序列化
                     String s = JSONObject.toJSONString(student);
                     //字符串轉(zhuǎn)回java對(duì)象
                     JSONObject.parseObject(s, Student.class);
                 });
                 // 將學(xué)生list序列化,之后轉(zhuǎn)為jsonArray
                 JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(studentList));
                 // 將jsonArray轉(zhuǎn)java對(duì)象list
                 jsonArray.toJavaList(Student.class);
                 //單次處理時(shí)間
                 long endTime = System.currentTimeMillis();
                 // 單次耗時(shí)
                 totalTime += (endTime - startTime);
                 System.out.println("單次耗費(fèi)時(shí)間:" + (endTime - startTime) + "ms");
             }
             System.out.println("平均耗費(fèi)時(shí)間:" + totalTime / NUM + "ms");
          }
          上述代碼在fastjson和fastjson2的測試中基本相同,唯一不同在于在fastjson2當(dāng)中,jsonArray.toJavaList方法轉(zhuǎn)變成了jsonArray.toList。

          二、性能測試

          本節(jié)將使用上面的代碼進(jìn)行測試。在此之前,我們首先需要針對(duì)兩個(gè)子工程設(shè)置相同的堆空間大小128M,以免造成偏差:
          fastjson2性能測試

          2.1 第一次測試

          下面正是開始測試:
          單次耗費(fèi)時(shí)間:863ms
          單次耗費(fèi)時(shí)間:444ms
          單次耗費(fèi)時(shí)間:424ms
          單次耗費(fèi)時(shí)間:399ms
          單次耗費(fèi)時(shí)間:384ms
          單次耗費(fèi)時(shí)間:355ms
          單次耗費(fèi)時(shí)間:353ms
          單次耗費(fèi)時(shí)間:363ms
          ... ...
          單次耗費(fèi)時(shí)間:361ms
          單次耗費(fèi)時(shí)間:356ms
          單次耗費(fèi)時(shí)間:355ms
          單次耗費(fèi)時(shí)間:357ms
          單次耗費(fèi)時(shí)間:351ms
          單次耗費(fèi)時(shí)間:354ms
          平均耗費(fèi)時(shí)間:366ms
          如上所示,除了第一次很慢,第二次變快,到最后基本穩(wěn)定在360毫秒左右,最終的平均耗時(shí)是366ms。
          單次耗費(fèi)時(shí)間:957ms
          單次耗費(fèi)時(shí)間:803ms
          單次耗費(fèi)時(shí)間:468ms
          單次耗費(fèi)時(shí)間:435ms
          單次耗費(fèi)時(shí)間:622ms
          單次耗費(fèi)時(shí)間:409ms
          單次耗費(fèi)時(shí)間:430ms
          ··· ···
          單次耗費(fèi)時(shí)間:400ms
          單次耗費(fèi)時(shí)間:641ms
          單次耗費(fèi)時(shí)間:403ms
          單次耗費(fèi)時(shí)間:398ms
          單次耗費(fèi)時(shí)間:431ms
          單次耗費(fèi)時(shí)間:356ms
          單次耗費(fèi)時(shí)間:362ms
          單次耗費(fèi)時(shí)間:626ms
          單次耗費(fèi)時(shí)間:404ms
          單次耗費(fèi)時(shí)間:395ms
          平均耗費(fèi)時(shí)間:478ms
          如上所示,首次執(zhí)行慢,逐步變快,但是后面就出現(xiàn)問題了,怎么執(zhí)行的時(shí)間這么不穩(wěn)定?跨度從390多到640多?這是怎么回事?平均時(shí)間也達(dá)到了478ms,反而比fastjson還要慢。

          2.2 fastjson2慢的原因?

          比較熟悉java的應(yīng)該都能想到一個(gè)問題:「由于堆空間大小不夠,導(dǎo)致頻繁發(fā)生GC,最終導(dǎo)致處理時(shí)間增長?」
          帶著這個(gè)推測,我們使用 jvisualVM 來看下在fastjson2執(zhí)行時(shí),內(nèi)存的使用情況,使用如下方式啟動(dòng):

          如上所示的啟動(dòng)放肆?xí)苯哟蜷_ jvisualvm 的控制面板,選擇 Visual GC,最終結(jié)果如下所示:
          Visual GC
          如上所示有幾處重點(diǎn),單獨(dú)看下:

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

          Full GC
          如上所示,老年代發(fā)生了316次GC,耗時(shí)27.225s。
          通過上面的觀察,基本可以確定由于「GC導(dǎo)致了fastjson2整體處理時(shí)間變長」。

          2.3 fastjson的GC表現(xiàn)

          我們可以再看下fastjson當(dāng)中的gc是什么樣的:
          如上可知,fastjson1中發(fā)生了1675次gc,與fastjson2相比少了139次,并且時(shí)間少了11.55s。
          通過前面測試的結(jié)果,fastjson1平均時(shí)間366ms,而fastjson2是478ms,分別乘以100次,能夠得到如下的時(shí)間差:
          (478?100?366?100)/1000 =11.2(478*100 - 366*100)/1000 = 11.2
          與gc時(shí)間差11.55相差無幾,那么我們可以得到一個(gè)結(jié)論:「fastjson2的性能表現(xiàn),與堆空間的大小相關(guān)!」

          2.4 第二次試驗(yàn)

          我們似乎得到了一個(gè)結(jié)論,但是如何確定是fastjson2的那個(gè)方法消耗更多的內(nèi)存空間呢?畢竟我們?cè)跍y試方法中,調(diào)用了很多的方法。
          所以我們進(jìn)一步調(diào)小內(nèi)存,看看是否會(huì)有內(nèi)存溢出呢?
          我們將內(nèi)存調(diào)整為64M:
          -Xms64m -Xmx64m
          運(yùn)行后發(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)在如下代碼:

          插眼debug
          結(jié)論:「在toJsonString方法時(shí),發(fā)生了內(nèi)存溢出異常?!?/strong>

          2.5 第三次實(shí)驗(yàn)

          下面我們將內(nèi)存增大,看看是否能夠提升fastjson2的性能。將堆空間大小調(diào)整為256M。
          單次耗費(fèi)時(shí)間:805ms
          單次耗費(fèi)時(shí)間:224ms
          單次耗費(fèi)時(shí)間:235ms
          單次耗費(fèi)時(shí)間:228ms
          單次耗費(fèi)時(shí)間:222ms
          ... ...
          單次耗費(fèi)時(shí)間:191ms
          單次耗費(fèi)時(shí)間:196ms
          單次耗費(fèi)時(shí)間:193ms
          單次耗費(fèi)時(shí)間:194ms
          單次耗費(fèi)時(shí)間:192ms
          平均耗費(fèi)時(shí)間:198ms
          如上所示,發(fā)現(xiàn)隨著堆空間增加,fastjson1有較大的性能提升,平均時(shí)長在198ms。
          單次耗費(fèi)時(shí)間:671ms
          單次耗費(fèi)時(shí)間:496ms
          單次耗費(fèi)時(shí)間:412ms
          單次耗費(fèi)時(shí)間:405ms
          單次耗費(fèi)時(shí)間:315ms
          單次耗費(fèi)時(shí)間:321ms
          ... ...
          單次耗費(fèi)時(shí)間:337ms
          單次耗費(fèi)時(shí)間:326ms
          平均耗費(fèi)時(shí)間:335ms
          如上所示,結(jié)果在335毫秒,隨著內(nèi)存增加,性能有提升,但是仍然沒有fastjson1快。
          通過如上的實(shí)驗(yàn),我們似乎可以得到如下的結(jié)論:「在數(shù)據(jù)量較大時(shí),fastjson的性能還要好于fastjson2!」

          2.6 第四次試驗(yàn)

          本次測試我們要給足夠大堆空間,看看這兩者的性能表現(xiàn),此處將堆空間設(shè)置成1g
          -Xms1g -Xmx1g
          單次耗費(fèi)時(shí)間:943ms
          單次耗費(fèi)時(shí)間:252ms
          單次耗費(fèi)時(shí)間:156ms
          單次耗費(fèi)時(shí)間:155ms
          ... ...
          單次耗費(fèi)時(shí)間:119ms
          單次耗費(fèi)時(shí)間:114ms
          單次耗費(fèi)時(shí)間:108ms
          單次耗費(fèi)時(shí)間:133ms
          單次耗費(fèi)時(shí)間:115ms
          平均耗費(fèi)時(shí)間:133ms
          如上所示,在足夠大的內(nèi)存條件下,fastjson的平均時(shí)間達(dá)到了133ms。
          單次耗費(fèi)時(shí)間:705ms
          單次耗費(fèi)時(shí)間:199ms
          單次耗費(fèi)時(shí)間:172ms
          ... ...
          單次耗費(fèi)時(shí)間:101ms
          單次耗費(fèi)時(shí)間:124ms
          單次耗費(fèi)時(shí)間:96ms
          平均耗費(fèi)時(shí)間:119ms
          如上所示,fastjson2處理速度首次高于fastjson。

          2.7 小結(jié)

          通過前面的測試,我們能夠得到如下的結(jié)論:

          三、源碼分析

          本節(jié)將通過閱讀源碼的方式簡單了解fastjson2的原理,主要分為兩個(gè)方面進(jìn)行閱讀:
          「為什么通過這兩個(gè)方面?」
          fastjson的核心就是將java對(duì)象序列化成json(對(duì)應(yīng)writer),以及將json反序列化成java對(duì)象(對(duì)應(yīng)reader)。而且其內(nèi)部正是通過這樣的命名方式去實(shí)現(xiàn)的。

          3.1 序列化 writer

          toJSONString方法

          其實(shí)所謂的序列化,就是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會(huì)將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, nullnull0);
                  }
                  return writer.toString();
              }
          }

          defaultObjectWriterProvider對(duì)象

          查看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,其實(shí)現(xiàn)方式默認(rèn)是「基于ASM的動(dòng)態(tài)字節(jié)碼」實(shí)現(xiàn)。
          另外還提供了 反射lambda 的方式。
          到此為止已經(jīng)獲取到了ObjectWriterProvider,它的作用是用來獲取ObjectWriter的。

          getObjectWriter方法

          ObjectWriter的作用就是將java對(duì)象寫入到j(luò)son當(dāng)中,所以我們下面開始關(guān)注這一行代碼的實(shí)現(xiàn):
          writeContext.provider.getObjectWriter(valueClass, valueClass, fieldBased);
          繼續(xù)查看getObjectWriter方法,查看關(guān)鍵位置代碼:
          if (objectWriter == null) {
              // 獲取creator,此處獲取的是方法開始時(shí)默認(rèn)的【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對(duì)象當(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方法獲取到對(duì)象的屬性名,找到屬性后,創(chuàng)建對(duì)應(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方法后,會(huì)得到一個(gè)全部屬性的List fieldWriters集合:
          fieldWriters = new ArrayList<>(fieldWriterMap.values());
          再往后,fastjson2會(huì)組裝一個(gè)動(dòng)態(tài)類:【ObjectWriter_1】,在里面組裝能夠?qū)懭隞SONWriter的各種屬性和方法,以及get屬性獲取:

          fastjson2
          定義和初始化此對(duì)象的方法如下所示:
          //定義【ObjectWriter_1】的屬性
          genFields(fieldWriters, cw);

          // 定義【ObjectWriter_1】的方法
          genMethodInit(fieldWriters, cw, classNameType);
          //定義【ObjectWriter_1】獲取對(duì)象屬性的讀取方法
          genGetFieldReader(
                  fieldWriters,
                  cw,
                  classNameType,
                  new ObjectWriterAdapter(objectClass, nullnull, features, fieldWriters)
          );
          此動(dòng)態(tài)對(duì)象的末尾【1】是隨數(shù)量增長的。
          繼續(xù)向下跟蹤到如下方法:
          genMethodWrite(objectClass, fieldWriters, cw, classNameType, writerFeatures);
          此方法主要的作用是創(chuàng)建【ObjectWrite_1】的write方法,并匹配當(dāng)前java對(duì)象的屬性屬于哪種類型,使用哪種FieldWriter進(jìn)行寫入。
          其內(nèi)部會(huì)輪詢所有的屬性進(jìn)行匹配,我們的屬性主要是StringInteger,如下:
          ... ...
           else if (fieldClass == Integer.class{
               // 處理Integer屬性
              gwInt32(mwc, fieldWriter, OBJECT, i);
          else if (fieldClass == String.class{
              // 處理String屬性
              gwFieldValueString(mwc, fieldWriter, OBJECT, i);
          }
          ... ...
          再向下會(huì)通過以下方法修改寫入不同類型屬性的方法名稱和描述信息等
          genMethodWriteArrayMapping("writeArrayMapping", objectClass, writerFeatures, fieldWriters, cw, classNameType);
          能夠看到,Integer和String的后續(xù)處理方法不同:
          else if (fieldClass == String.class{
              methodName = "writeString";
              methodDesc = "(Ljava/lang/String;)V";

          到此整個(gè)ObjectWriter_1對(duì)象就設(shè)置完成了,使用反射進(jìn)行創(chuàng)建:
          try {
              Constructor<?> constructor = deserClass.getConstructor(Class.classString.classString.classlong.classList.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對(duì)象的屬性,并成功創(chuàng)建了【ObjectWriter】:

          ObjectWriter
          再返回toJSonString方法當(dāng)中,看看Object的后續(xù)操作 拿到的ObjectWriter調(diào)用其【write】方法進(jìn)行數(shù)據(jù)寫入:
          objectWriter.write(writer, object, nullnull0);
          我們已經(jīng)知道不同類型屬性使用不同的FieldWriter進(jìn)行寫入:
          關(guān)于具體的寫入過程就不在介紹了。

          小結(jié)

          官方提供Writer關(guān)系圖如下:

          Writer關(guān)系圖
          本節(jié)主要針對(duì)主要流程進(jìn)行梳理,與上圖對(duì)比存在部分未講解流程,感興趣同學(xué)參照源碼自行閱讀。
          整個(gè)過程較為復(fù)雜,簡單描述為:「使用ASM動(dòng)態(tài)字節(jié)碼方式作為基礎(chǔ),通過java對(duì)象的getter方法獲取對(duì)象的屬性值,構(gòu)建動(dòng)態(tài)ObjectWriter對(duì)象,針對(duì)不同的對(duì)象屬性,生成不同的寫入方法,最終通過反射進(jìn)行對(duì)象創(chuàng)建,最后進(jìn)行java對(duì)象數(shù)據(jù)的寫入?!?/strong>
          「值得一提的是,ObejctWriter對(duì)象是會(huì)進(jìn)行緩存的,有助于性能的提升。」

          3.2 反序列化 reader

          下面來看看反序列化reader的流程。因?yàn)榇篌w流程與writer差不多,所以以下內(nèi)容不做詳細(xì)講解了。

          parseObject 方法

          /**
           * json轉(zhuǎn)換java對(duì)象
           *
           * @param text  json字符串
           * @param 需要轉(zhuǎn)換的類
           * @return Class
           */

          @SuppressWarnings("unchecked")
          static <T> parseObject(String text, Class<T> clazz) {
              if (text == null || text.isEmpty()) {
                  return null;
              }
                  //創(chuàng)建reader,內(nèi)部與writer相同,使用ASM動(dòng)態(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對(duì)象,
          public static JSONReader of(String str) {
              if (str == null) {
                  throw new NullPointerException();
              }
              //創(chuàng)建reader的上下文,內(nèi)部與writer相同,使用ASM動(dòng)態(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對(duì)象
              return new JSONReaderUTF16(context, str, chars, 0, length);
          }

          getObjectReader方法

          與getObjectWriter類似,獲取動(dòng)態(tài)的json數(shù)據(jù)讀取對(duì)象。關(guān)注重點(diǎn)代碼:
          if (objectReader == null) {
              // 獲取前面創(chuàng)建的creater
              ObjectReaderCreator creator = getCreator();
              // 創(chuàng)建ObjectReader對(duì)象,根據(jù)java類的類型
              objectReader = creator.createObjectReader(objectClass, objectType, fieldBased, modules);
          }

          createObjectReader方法

          關(guān)注下面這行代碼:
          // 創(chuàng)建屬性讀取對(duì)象數(shù)組
          FieldReader[] fieldReaderArray = createFieldReaders(objectClass, objectType, beanInfo, fieldBased, modules);
          繼續(xù)跟進(jìn),發(fā)現(xiàn)遍歷java對(duì)象的setter方法,此時(shí)我們應(yīng)該能夠想到,向?qū)ο笤O(shè)置值的時(shí)候,一定是使用的setter方法:
          BeanUtils.setters(objectClass, method -> {
              fieldInfo.init();
              // 創(chuàng)建Fieldreader
              createFieldReader(objectClass, objectType, namingStrategy, orders, fieldInfo, method, fieldReaders, modules);
          });
          createFieldReader方法會(huì)獲取java對(duì)象當(dāng)中的屬性,以及set開頭的方法。
          處理完對(duì)象的屬性和set方法后,會(huì)生成ObjectReader對(duì)象進(jìn)行返回:

          ObjectReader
          此對(duì)象包含setterFieldReaders,用于向java對(duì)象寫入數(shù)據(jù)。

          回到parseObject

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

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

          小結(jié)

          官方提供的Reader關(guān)系圖:

          Reader關(guān)系圖
          感興趣的同學(xué)可以參考上圖的內(nèi)容,結(jié)合本文提供的流程,自己跟蹤一遍源碼。
          整個(gè)過成簡單描述:「底層使用ASM動(dòng)態(tài)字節(jié)碼為基礎(chǔ),通過java對(duì)象的setter方法去構(gòu)建動(dòng)態(tài)的ObjectReader對(duì)象,最終通過構(gòu)造器去創(chuàng)建一個(gè)新的java對(duì)象」

          四、總結(jié)

          關(guān)于fastjson2的簡單測試,以及源碼閱讀到此就告一段落了。
          針對(duì)fastjson2有以下幾點(diǎn)總結(jié):
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>fastjson</artifactId>
              <version>2.0.8</version>
          </dependency>
          但是官方也不保證100%兼容。

          bug
          全部源碼幾乎沒有注釋信息
          拋開上述存在的問題,fastjson2確實(shí)有不錯(cuò)的性能提升,通過官方提供的測試數(shù)據(jù)https://alibaba.github.io/fastjson2/benchmark_cn可以看得出來,感興趣可以本地實(shí)測一下。

          到此為止關(guān)于fastjson2的介紹就結(jié)束了,感謝大家的觀看。
          往期熱門文章:

          1、計(jì)算機(jī)專業(yè)會(huì)不會(huì)成為下一個(gè)土木?
          2、xxl-job驚艷的設(shè)計(jì),怎能叫人不愛
          3、ArrayList#subList這四個(gè)坑,一不小心就中招
          4、面試官:大量請(qǐng)求 Redis 不存在的數(shù)據(jù),從而影響數(shù)據(jù)庫,該如何解決?
          5、MySQL 暴跌!
          6、超越 Xshell!號(hào)稱下一代 Terminal 終端神器,用完愛不釋手!
          7、IDEA 官宣全新默認(rèn) UI,太震撼了??!
          8、讓你直呼「臥槽」的 GitHub 項(xiàng)目!
          9、Kafka又笨又重,為啥不選Redis?
          10、50多個(gè)高頻免費(fèi) API 接口分享

          瀏覽 42
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    免费日韩亚洲电影黄色 | 18禁黄无码| 私人女仆扫地偷懒被主人颜色吃现在被喷尿洗脸 | 青青草超碰大香蕉 | 黄色成人视频网站在线观看 |