1. 程式人生 > >alibaba fastjson(json序列化器)序列化部分原始碼解析- Java綜合

alibaba fastjson(json序列化器)序列化部分原始碼解析- Java綜合

fastjson官方地址: http://code.alibabatech.com/wiki/display/FastJSON/Home
    從javaeye上看到了阿里一位人士寫的fastjson,特別是其中如何將java物件序列化成json字串這段。筆者比較關注,因為在筆者的專案中就用了一個json序列化器(造的輪子)。就下載下來看了一看,先不說和筆者所用的輪子有何區別,單就用了一個簡單的測試器,來測試一下兩者的處理速度。測試程式碼就不貼了,簡單地說下測試結果。在jvm充分優化的情況下(for迴圈執行了很多次之後),筆者所使用的java序列化器處理速度不是很均勻,在結尾有短暫的變化(可能與虛擬機器回收有關係);而fastjson在後面的處理過程當中,一般很均勻(後來發現與使用的buf分配方式有關)。最主要的區別莫在於,fastjson的速度那是不能對比了。
    經過分析原始碼之後,發現fastjson在處理json優化上面還是下了很大的工夫的。筆者準備從以下幾個方面對fastjson作一個簡單的解析,也讓使用fastjson的同學對fastjson有一個簡單的認識。
        1    總體分析    分析json序列化的總體思路和解析過程
        2    效能分析A  針對字元生產部分(即outWriter)對不同型別資料的處理和與效能相關處理部分
        3    性別分析B  針對序列化過程部分(即objectSerializer)對不同型別的序列化過程處理和與效能相關處理部分
        4    物件解析分析    對javaBean解析部分和針對欄位輸出部分的處理和解析
    原始碼分析基於1.0.5版本。

    總體分析,首先上圖,即fastjson的總體處理思想,其實也是所有json序列化器需要考慮的問題。

    在這裡,需要考慮的主要有兩個部分,一是臨時儲存在序列化過程中用於儲存資料的容器,二是處理物件序列化的序列化器。
    在fastjson中,儲存資料的容器使用了wirter,字元輸出流,而且是自實現的一個字元輸出流。相對原來的writer,追加了很多需要輸出的資訊的實現,比如輸出一個字串,輸出一個字元,輸出一個long型別資料等。而處理物件序列化的序列化器,而使用了責任鏈模式和工廠模式,將不同型別的java物件分散到不同的序列化器當中。而每個序列化器只處理與自身型別相對應的資料資訊,這樣就避免了在處理時,各種情況交織在一塊,邏輯混亂的問題。
    下面就原始碼本身作一個分析,其中結合兩個部分進行分析。

    程式碼分析部分

    首先,將一個物件序列化json字串呼叫的是JSON物件的toJSONString方法,這裡呼叫的是無引數方法。(注:本文不分析採用vistor實現json序列化程式碼的部分)。具體程式碼如下所示:

第一行,新產生的一個數據儲存器,儲存在序列化過程中產生的資料;第二行,產生統一的json序列化器,其中使用了outWriter,此類即json序列化的統一處理器;第三行,呼叫序列化方法開始序列化物件,以產生json字串資訊;第四行,返回已經儲存的json資訊。

Java程式碼  收藏程式碼
  1. SerializeWriter out = new SerializeWriter();  
  2.        JSONSerializer serializer = new JSONSerializer(out);  
  3.        serializer.write(object);  
  4.        return out.toString();  

    資料儲存器(序列化輸出容器)
   
    SerializeWriter是一個用於儲存在序列化過程中產生的資料資訊,它與jdk中的StringBuiler有著類似的功能,即將不同的資料填充到此容器中。之所以不使用StringBuilder的原因之一在於StringBuilder沒有提供一些特別為效能優化的方法,並且StringBuilder在處理過程中增加了多餘的操作(如新分配物件)。該容器的主要功能就是接收不同的資料,並將這些資料儲存到該內部的一個字元陣列當中,同時記錄字元總數。
    既然充當了一個數據輸出的角色,那麼就可以往其中輸入任何的資料,包括int,byte,short等基本型別和對應的包裝型別,也包括日期資料,以及經常使用的字串資料等。對於在這些資料型別之外的其它型別,由於json的特殊結構,所有的高階型別均可以轉化於這些基礎型別的組織體,所以不需要再針對高階型別作處理了(這些是序列化器應該考慮的問題)。

    首先對SerializeWriter的方法(輸出和追加)作一個預覽:

方法總共可以分五個部分,第一個部分是針對writer基本功能一個擴充套件,即支援輸出int,字元,以及字元陣列,追加字元陣列(包括字串)等;第二個部分提供了寫整形和長整形的基本方法;第三個部分是提供寫基本資料型別陣列的支援;第四個部分是提供寫一個數字+一個字元的形式,比如資料+[,\],}]這種格式;第五個部分是提供寫資料串,主是是針對字串追加雙引號或單引號(以支援標準json)。

Java程式碼  收藏程式碼
  1. public void write(int c)  
  2. public void write(char c)  
  3. public void write(char c[], int off, int len)  
  4. public void write(String str, int off, int len)  
  5. public SerializeWriter append(CharSequence csq)  
  6. public SerializeWriter append(CharSequence csq, int start, int end)  
  7. public SerializeWriter append(char c)  
  8. public void writeInt(int i)  
  9. public void writeLong(long i)  
  10. public void writeBooleanArray(boolean[] array)  
  11. public void writeShortArray(short[] array)  
  12. public void writeByteArray(byte[] array)  
  13. public void writeIntArray(int[] array)  
  14. public void writeIntArray(Integer[] array)  
  15. public void writeLongArray(long[] array)  
  16. public void writeIntAndChar(int i, char c)  
  17. public void writeLongAndChar(long i, char c)  
  18. public void writeStringWithDoubleQuote(String text)  
  19. public void writeKeyWithDoubleQuote(String text)  
  20. public void writeStringWithSingleQuote(String text)  
  21. public void writeStringArray(String[] array)  
  22. public void writeKeyWithSingleQuote(String text)  
  23. public void writeKeyWithDoubleQuoteIfHashSpecial(String text)  
  24. public void writeKeyWithSingleQuoteIfHashSpecial(String text)  

    五個部分的方法,每個部分都有其特殊的作用和意義,針對最常用的數字和字串作了特別的對待。

    在實現上面,SerializeWriter使用了一個內部的字元陣列作為資料的儲存器,同時使用了一個計數器計算當前儲存的字元量。既然使用了字元陣列,那麼肯定有相關的操作,如字元擴容等。整個寫資料的過程,其實就是往這個字元陣列追加資料的過程,需要考慮只是如何追加資料的問題,即上面所列出的這麼多些方法。在最終寫完資料之後,即可將這個字元陣列轉為我們所需要的字串了。

    物件序列化入口

    JsonSerializer,準備地講,這個類不應該叫這個名字,因為它與其它的物件序列化器相混淆了。這只是一個提供物件序列化的一個入口;同時,它持有所有具體負責物件序列化工作類的引用。將這些序列化器集中起來,需要用到哪個物件序列化器時,就取出這個序列化器,並呼叫相應的序列化方法。
    既然是物件序列化入口,它就需要關注兩個事情。一是我們究竟有哪些序列化器可以使用,二是對於一個物件,應該使用哪一個序列化器來進行工作。對於這兩個問題,JsonSerializer內部持有一個JSONSerializerMap的屬性,即表示應該序列化的物件型別和對應的序列化器的一個對映。我們來看預設的構造方法,它使用了預設的全域性物件型別和物件序列化器對映:

Java程式碼  收藏程式碼
  1. public JSONSerializer(SerializeWriter out){  
  2.         this(out, JSONSerializerMap.getGlobalInstance());  
  3.     }  
  這時使用了全域性的一個物件序列化器對映,加上後面在getObjectWriter中追加的物件序列化器對映。在整個jsonSerializer中,可以使用的物件序列化器有以下這些: Java程式碼  收藏程式碼
  1. put(Boolean.class, BooleanSerializer.instance);  
  2.         put(Byte.class, ByteSerializer.instance);  
  3.         put(Short.class, ShortSerializer.instance);  
  4.         put(Integer.class, IntegerSerializer.instance);  
  5.         put(Long.class, LongSerializer.instance);  
  6.         put(Float.class, FloatSerializer.instance);  
  7.         put(Double.class, DoubleSerializer.instance);  
  8.         put(BigDecimal.class, BigDecimalSerializer.instance);  
  9.         put(BigInteger.class, BigIntegerSerializer.instance);  
  10.         put(String.class, StringSerializer.instance);  
  11.         put(byte[].class, ByteArraySerializer.instance);  
  12.         put(short[].class, ShortArraySerializer.instance);  
  13.         put(int[].class, IntArraySerializer.instance);  
  14.         put(long[].class, LongArraySerializer.instance);  
  15.         put(float[].class, FloatArraySerializer.instance);  
  16.         put(double[].class, DoubleArraySerializer.instance);  
  17.         put(boolean[].class, BooleanArraySerializer.instance);  
  18.         put(Integer[].class, IntegerArraySerializer.instance);  
  19.         put(String[].class, StringArraySerializer.instance);  
  20.         put(Object[].class, ObjectArraySerializer.instance);  
  21.         put(Class.class, ClassSerializer.instance);  
  22.         // atomic  
  23.         put(AtomicBoolean.class, AtomicBooleanSerializer.instance);  
  24.         put(AtomicInteger.class, AtomicIntegerSerializer.instance);  
  25.         put(AtomicLong.class, AtomicLongSerializer.instance);  
  26.         put(AtomicReference.class, AtomicReferenceSerializer.instance);  
  27.         put(AtomicIntegerArray.class, AtomicIntegerArraySerializer.instance);  
  28.         put(AtomicLongArray.class, AtomicLongArraySerializer.instance);  
  29.         // jmx  
  30.         put(CompositeData.class, CompositeDataSerializer.instance);  
  31.         put(CompositeDataSupport.class, CompositeDataSerializer.instance);  
  32.         put(TabularData.class, TabularDataSerializer.instance);  
  33.         put(TabularDataSupport.class, TabularDataSerializer.instance);  
  34.         put(ObjectName.class, ObjectNameSerializer.instance);  
  35.         put(SimpleType.class, SimpleTypeSerializer.instance);  
  36.         //在執行過程中追加部分  
  37.                 mapping.put(clazz, MapSerializer.instance);  
  38.                 mapping.put(clazz, ListSerializer.instance);  
  39.                 mapping.put(clazz, CollectionSerializer.instance);  
  40.                 mapping.put(clazz, DateSerializer.instance);  
  41.                 mapping.put(clazz, JSONAwareSerializer.instance);  
  42.                 mapping.put(clazz, JSONStreamAwareSerializer.instance);  
  43.                 mapping.put(clazz, EnumSerializer.instance);  
  44.                 mapping.put(clazz, new ArraySerializer(compObjectSerializer));  
  45.                 mapping.put(clazz, new ExceptionSerializer(clazz));  
  46.                 mapping.put(clazz, new JavaBeanSerializer(clazz));  

     這些序列化器,覆蓋了基本資料,字串型別,日期,以及集合,map,以及javaBean的所有序列化器。因為不存在沒有匹配不了的序列化器。既然有個序列化器,就可以執行序列化工作了。即到了序列化入口應該做的工作了。

Java程式碼  收藏程式碼
  1. Class<?> clazz = object.getClass();  
  2.             ObjectSerializer writer = getObjectWriter(clazz);  
  3.             writer.write(this, object);  

     首先獲得當前序列化物件所在的型別,再根據型別取得相對應的序列化器,最後使用序列化器進行正式的序列化工作。

    序列化過程

    正如上面所說,進入序列化工作之後,即是針對每一種型別進行序列化處理了。該序列化工作使用了統一的方法,即實現了統一的序列化方法:

Java程式碼  收藏程式碼
  1. void write(JSONSerializer serializer, Object object) throws IOException  

    該方法在抽象類(可以說是介面)ObjectSerializer中定義,即所有的序列化器都繼承了此類,並實現了此方法用於處理不同的情形。對於上層呼叫(如JsonSerializer),不需要考慮每一個型別的序列化工作是如何實現的,只需要針對不同的型別找到正確的序列化器,進行序列化工作即可。

    對於一個序列化器,通常的工作,是首先取得當前的資料儲存容器,然後根據不同的物件型別,將物件輸出到outWriter中即可。比如一個序列化實現IntergerSerializer,它的實現如下:

Java程式碼  收藏程式碼
  1. SerializeWriter out = serializer.getWrier();  
  2.         Integer value = (Integer) object;  
  3.         out.writeInt(value.intValue());  

    這 樣即完成了一個完整的序列化工作。當然,對於複雜的資料型別,在實現過程中,可能需要遞迴地呼叫JsonSerializer的序列化工作,這得歸結於如何處理不同的物件型別了。比如處理一個物件集合時,除需要處理集合本身之外,還需要處理集合中的每一個物件,這時又是一個解析過程。由於使用了同一個jsonSerializer,所以在進行資料處理時,輸出的資料會按照在解析過程中的順序,順序地寫入到outWriter中,這樣即保證了資料的正確性。

總結

    整個解析過程,相對來說,比較地簡單。因為,這個解析工作從原理上來講,也並不複雜。困難地在於,如何處理不同的資料型別,以及在處理過程中如何保證處理的效率。這即是fastjson之所以產生的原因。
    本篇從整個結構出發,對fastjson中的json序列化過程有了一個初步的理解,讓大家都能夠很好地正解fastjson,包括fastjson本身在實現上可能存在的不合理情況。在下一篇中,就效率實現上的兩個重要方面(輸出效率和解析過程)分別進行解析。