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程式碼-
SerializeWriter out = new SerializeWriter();
- JSONSerializer serializer = new JSONSerializer(out);
- serializer.write(object);
- return out.toString();
資料儲存器(序列化輸出容器)
SerializeWriter是一個用於儲存在序列化過程中產生的資料資訊,它與jdk中的StringBuiler有著類似的功能,即將不同的資料填充到此容器中。之所以不使用StringBuilder的原因之一在於StringBuilder沒有提供一些特別為效能優化的方法,並且StringBuilder在處理過程中增加了多餘的操作(如新分配物件)。該容器的主要功能就是接收不同的資料,並將這些資料儲存到該內部的一個字元陣列當中,同時記錄字元總數。
既然充當了一個數據輸出的角色,那麼就可以往其中輸入任何的資料,包括int,byte,short等基本型別和對應的包裝型別,也包括日期資料,以及經常使用的字串資料等。對於在這些資料型別之外的其它型別,由於json的特殊結構,所有的高階型別均可以轉化於這些基礎型別的組織體,所以不需要再針對高階型別作處理了(這些是序列化器應該考慮的問題)。
首先對SerializeWriter的方法(輸出和追加)作一個預覽:
方法總共可以分五個部分,第一個部分是針對writer基本功能一個擴充套件,即支援輸出int,字元,以及字元陣列,追加字元陣列(包括字串)等;第二個部分提供了寫整形和長整形的基本方法;第三個部分是提供寫基本資料型別陣列的支援;第四個部分是提供寫一個數字+一個字元的形式,比如資料+[,\],}]這種格式;第五個部分是提供寫資料串,主是是針對字串追加雙引號或單引號(以支援標準json)。
Java程式碼- public void write(int c)
- public void write(char c)
- public void write(char c[], int off, int len)
- public void write(String str, int off, int len)
- public SerializeWriter append(CharSequence csq)
- public SerializeWriter append(CharSequence csq, int start, int end)
- public SerializeWriter append(char c)
- public void writeInt(int i)
- public void writeLong(long i)
- public void writeBooleanArray(boolean[] array)
- public void writeShortArray(short[] array)
- public void writeByteArray(byte[] array)
- public void writeIntArray(int[] array)
- public void writeIntArray(Integer[] array)
- public void writeLongArray(long[] array)
- public void writeIntAndChar(int i, char c)
- public void writeLongAndChar(long i, char c)
- public void writeStringWithDoubleQuote(String text)
- public void writeKeyWithDoubleQuote(String text)
- public void writeStringWithSingleQuote(String text)
- public void writeStringArray(String[] array)
- public void writeKeyWithSingleQuote(String text)
- public void writeKeyWithDoubleQuoteIfHashSpecial(String text)
- public void writeKeyWithSingleQuoteIfHashSpecial(String text)
五個部分的方法,每個部分都有其特殊的作用和意義,針對最常用的數字和字串作了特別的對待。
在實現上面,SerializeWriter使用了一個內部的字元陣列作為資料的儲存器,同時使用了一個計數器計算當前儲存的字元量。既然使用了字元陣列,那麼肯定有相關的操作,如字元擴容等。整個寫資料的過程,其實就是往這個字元陣列追加資料的過程,需要考慮只是如何追加資料的問題,即上面所列出的這麼多些方法。在最終寫完資料之後,即可將這個字元陣列轉為我們所需要的字串了。
物件序列化入口
JsonSerializer,準備地講,這個類不應該叫這個名字,因為它與其它的物件序列化器相混淆了。這只是一個提供物件序列化的一個入口;同時,它持有所有具體負責物件序列化工作類的引用。將這些序列化器集中起來,需要用到哪個物件序列化器時,就取出這個序列化器,並呼叫相應的序列化方法。
既然是物件序列化入口,它就需要關注兩個事情。一是我們究竟有哪些序列化器可以使用,二是對於一個物件,應該使用哪一個序列化器來進行工作。對於這兩個問題,JsonSerializer內部持有一個JSONSerializerMap的屬性,即表示應該序列化的物件型別和對應的序列化器的一個對映。我們來看預設的構造方法,它使用了預設的全域性物件型別和物件序列化器對映:
- public JSONSerializer(SerializeWriter out){
- this(out, JSONSerializerMap.getGlobalInstance());
- }
- put(Boolean.class, BooleanSerializer.instance);
- put(Byte.class, ByteSerializer.instance);
- put(Short.class, ShortSerializer.instance);
- put(Integer.class, IntegerSerializer.instance);
- put(Long.class, LongSerializer.instance);
- put(Float.class, FloatSerializer.instance);
- put(Double.class, DoubleSerializer.instance);
- put(BigDecimal.class, BigDecimalSerializer.instance);
- put(BigInteger.class, BigIntegerSerializer.instance);
- put(String.class, StringSerializer.instance);
- put(byte[].class, ByteArraySerializer.instance);
- put(short[].class, ShortArraySerializer.instance);
- put(int[].class, IntArraySerializer.instance);
- put(long[].class, LongArraySerializer.instance);
- put(float[].class, FloatArraySerializer.instance);
- put(double[].class, DoubleArraySerializer.instance);
- put(boolean[].class, BooleanArraySerializer.instance);
- put(Integer[].class, IntegerArraySerializer.instance);
- put(String[].class, StringArraySerializer.instance);
- put(Object[].class, ObjectArraySerializer.instance);
- put(Class.class, ClassSerializer.instance);
- // atomic
- put(AtomicBoolean.class, AtomicBooleanSerializer.instance);
- put(AtomicInteger.class, AtomicIntegerSerializer.instance);
- put(AtomicLong.class, AtomicLongSerializer.instance);
- put(AtomicReference.class, AtomicReferenceSerializer.instance);
- put(AtomicIntegerArray.class, AtomicIntegerArraySerializer.instance);
- put(AtomicLongArray.class, AtomicLongArraySerializer.instance);
- // jmx
- put(CompositeData.class, CompositeDataSerializer.instance);
- put(CompositeDataSupport.class, CompositeDataSerializer.instance);
- put(TabularData.class, TabularDataSerializer.instance);
- put(TabularDataSupport.class, TabularDataSerializer.instance);
- put(ObjectName.class, ObjectNameSerializer.instance);
- put(SimpleType.class, SimpleTypeSerializer.instance);
- //在執行過程中追加部分
- mapping.put(clazz, MapSerializer.instance);
- mapping.put(clazz, ListSerializer.instance);
- mapping.put(clazz, CollectionSerializer.instance);
- mapping.put(clazz, DateSerializer.instance);
- mapping.put(clazz, JSONAwareSerializer.instance);
- mapping.put(clazz, JSONStreamAwareSerializer.instance);
- mapping.put(clazz, EnumSerializer.instance);
- mapping.put(clazz, new ArraySerializer(compObjectSerializer));
- mapping.put(clazz, new ExceptionSerializer(clazz));
- mapping.put(clazz, new JavaBeanSerializer(clazz));
這些序列化器,覆蓋了基本資料,字串型別,日期,以及集合,map,以及javaBean的所有序列化器。因為不存在沒有匹配不了的序列化器。既然有個序列化器,就可以執行序列化工作了。即到了序列化入口應該做的工作了。
Java程式碼- Class<?> clazz = object.getClass();
- ObjectSerializer writer = getObjectWriter(clazz);
- writer.write(this, object);
首先獲得當前序列化物件所在的型別,再根據型別取得相對應的序列化器,最後使用序列化器進行正式的序列化工作。
序列化過程
正如上面所說,進入序列化工作之後,即是針對每一種型別進行序列化處理了。該序列化工作使用了統一的方法,即實現了統一的序列化方法:
Java程式碼- void write(JSONSerializer serializer, Object object) throws IOException
該方法在抽象類(可以說是介面)ObjectSerializer中定義,即所有的序列化器都繼承了此類,並實現了此方法用於處理不同的情形。對於上層呼叫(如JsonSerializer),不需要考慮每一個型別的序列化工作是如何實現的,只需要針對不同的型別找到正確的序列化器,進行序列化工作即可。
對於一個序列化器,通常的工作,是首先取得當前的資料儲存容器,然後根據不同的物件型別,將物件輸出到outWriter中即可。比如一個序列化實現IntergerSerializer,它的實現如下:
Java程式碼- SerializeWriter out = serializer.getWrier();
- Integer value = (Integer) object;
- out.writeInt(value.intValue());
這 樣即完成了一個完整的序列化工作。當然,對於複雜的資料型別,在實現過程中,可能需要遞迴地呼叫JsonSerializer的序列化工作,這得歸結於如何處理不同的物件型別了。比如處理一個物件集合時,除需要處理集合本身之外,還需要處理集合中的每一個物件,這時又是一個解析過程。由於使用了同一個jsonSerializer,所以在進行資料處理時,輸出的資料會按照在解析過程中的順序,順序地寫入到outWriter中,這樣即保證了資料的正確性。
總結
整個解析過程,相對來說,比較地簡單。因為,這個解析工作從原理上來講,也並不複雜。困難地在於,如何處理不同的資料型別,以及在處理過程中如何保證處理的效率。這即是fastjson之所以產生的原因。
本篇從整個結構出發,對fastjson中的json序列化過程有了一個初步的理解,讓大家都能夠很好地正解fastjson,包括fastjson本身在實現上可能存在的不合理情況。在下一篇中,就效率實現上的兩個重要方面(輸出效率和解析過程)分別進行解析。