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版本。javascript
整體分析,首先上圖,即fastjson的整體處理思想,其實也是全部json序列化器須要考慮的問題。java
在這裏,須要考慮的主要有兩個部分,一是臨時保存在序列化過程當中用於儲存數據的容器,二是處理對象序列化的序列化器。
在fastjson中,保存數據的容器使用了wirter,字符輸出流,並且是自實現的一個字符輸出流。相對原來的writer,追加了不少須要輸出的信息的實現,好比輸出一個字符串,輸出一個字符,輸出一個long類型數據等。而處理對象序列化的序列化器,而使用了責任鏈模式和工廠模式,將不一樣類型的java對象分散到不一樣的序列化器當中。而每一個序列化器只處理與自身類型相對應的數據信息,這樣就避免了在處理時,各類狀況交織在一塊,邏輯混亂的問題。
下面就源碼自己做一個分析,其中結合兩個部分進行分析。web
代碼分析部分json
首先,將一個對象序列化json字符串調用的是JSON對象的toJSONString方法,這裏調用的是無參數方法。(注:本文不分析採用vistor實現json序列化代碼的部分)。具體代碼以下所示:數組
第一行,新產生的一個數據保存器,儲存在序列化過程當中產生的數據;第二行,產生統一的json序列化器,其中使用了outWriter,此類即json序列化的統一處理器;第三行,調用序列化方法開始序列化對象,以產生json字符串信息;第四行,返回已經儲存的json信息。性能優化
- 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的特殊結構,全部的高級類型都可以轉化於這些基礎類型的組織體,因此不須要再針對高級類型做處理了(這些是序列化器應該考慮的問題)。app
首先對SerializeWriter的方法(輸出和追加)做一個預覽:jvm
方法總共能夠分五個部分,第一個部分是針對writer基本功能一個擴展,即支持輸出int,字符,以及字符數組,追加字符數組(包括字符串)等;第二個部分提供了寫整形和長整形的基本方法;第三個部分是提供寫基本數據類型數組的支持;第四個部分是提供寫一個數字+一個字符的形式,好比數據+[,\],}]這種格式;第五個部分是提供寫數據串,主是是針對字符串追加雙引號或單引號(以支持標準json)。源碼分析
- 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());
- }
這時使用了全局的一個對象序列化器映射,加上後面在getObjectWriter中追加的對象序列化器映射。在整個jsonSerializer中,可使用的對象序列化器有如下這些:
- 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);
-
-
- 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);
-
-
- 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的全部序列化器。由於不存在沒有匹配不了的序列化器。既然有個序列化器,就能夠執行序列化工做了。即到了序列化入口應該作的工做了。
- Class<?> clazz = object.getClass();
- ObjectSerializer writer = getObjectWriter(clazz);
- writer.write(this, object);
首先得到當前序列化對象所在的類型,再根據類型取得相對應的序列化器,最後使用序列化器進行正式的序列化工做。
序列化過程
正如上面所說,進入序列化工做以後,便是針對每一種類型進行序列化處理了。該序列化工做使用了統一的方法,即實現了統一的序列化方法:
- void write(JSONSerializer serializer, Object object) throws IOException
該方法在抽象類(能夠說是接口)ObjectSerializer中定義,即全部的序列化器都繼承了此類,並實現了此方法用於處理不一樣的情形。對於上層調用(如JsonSerializer),不須要考慮每個類型的序列化工做是如何實現的,只須要針對不一樣的類型找到正確的序列化器,進行序列化工做便可。
對於一個序列化器,一般的工做,是首先取得當前的數據儲存容器,而後根據不一樣的對象類型,將對象輸出到outWriter中便可。好比一個序列化實現IntergerSerializer,它的實現以下:
- SerializeWriter out = serializer.getWrier();
- Integer value = (Integer) object;
- out.writeInt(value.intValue());
這 樣即完成了一個完整的序列化工做。固然,對於複雜的數據類型,在實現過程當中,可能須要遞歸地調用JsonSerializer的序列化工做,這得歸結於如何處理不一樣的對象類型了。好比處理一個對象集合時,除須要處理集合自己以外,還須要處理集合中的每個對象,這時又是一個解析過程。因爲使用了同一個jsonSerializer,因此在進行數據處理時,輸出的數據會按照在解析過程當中的順序,順序地寫入到outWriter中,這樣即保證了數據的正確性。
總結
整個解析過程,相對來講,比較地簡單。由於,這個解析工做從原理上來說,也並不複雜。困難地在於,如何處理不一樣的數據類型,以及在處理過程當中如何保證處理的效率。這便是fastjson之因此產生的緣由。 本篇從整個結構出發,對fastjson中的json序列化過程有了一個初步的理解,讓你們都可以很好地正解fastjson,包括fastjson自己在實現上可能存在的不合理狀況。在下一篇中,就效率實現上的兩個重要方面(輸出效率和解析過程)分別進行解析。