前面講了進行對象解析的兩個方面,並講了針對outWriter將不一樣類型的數據信息寫到buf字符數組。本篇講解對象解析的過程,即如何將不一樣類型的對象解析成outWriter所須要的序列信息。並考慮其中的性能優化。javascript
取得解析器
首先咱們須要取得指定對象的json序列化器,以便使用特定的序列化器來序列化對象。所以,須要有一個方法來取得相對應的序列化器。在fastjson中,使用了一個相似map的結構來保存對象類型和及對應的解析器。對於對象類型,在整個fastjson中,分爲如下幾類:
1 基本類型以及其包裝類型,字符串
2 基本類型數組以及包裝類型數組
3 Atomic類型
4 JMX類型
5 集合類型以及子類
6 時間類型
7 json類型
8 對象數組類型
9 javaBean類型java
對於第1,2,3,4類型,在fastjson中使用了一個全局的單態實例來保存相對應的解析器;第5類型,處理集合類型,對於集合類型及其,因爲其處理邏輯均是同樣,因此只須要針對子類做一些的處理,讓其返回相對應的集合類型解析器便可;第6類型,時間處理器,將時間轉化爲相似yyyy-MM-ddTHH:mm:ss.SSS的格式;第7類型,處理fastjson專有jsonAwre類型;第8類型,處理對象的數組形式,即處理數組時,須要考慮數組中的統一對象類型;第9,即處理咱們最常使用的對象,javaBean類型,這也是在項目中解析得最多的類型。web
咱們要看一下相對應的取解析器的方法,即類JsonSerializer.getObjectWriter(Class<?> clazz)方法,參考其中的實現:json
首先,取對象解析器是由一個類型爲JSONSerializerMap的對象mapping中取。之因此使用此類型,這是性能優化的一部分,此類型並非一個完整的map類型,只是實現了一個相似map的操做的類型。其內部並無使用基於equals的比較方式,而是使用了System.identityHashCode的實現。對於一個對象,其identityHashCode的值是定值,而對於一個類型,在整個jvm中只有一個,所以這裏使用基於class的identityHashCode是能夠了,也避免了使用class的euqlas來進行比較。
接着再根據每個類型從mapping中取出相對應的解析器。首先從繼承而來的全局解析器取得解析器,若是對象不屬於第1,2,3,4類型,而開始進入如下的if else階段。
咱們從上面的源碼中,能夠看出解析器主要分爲兩個部分,一個是與解析類型相關的,一個是無關的。好比對於第5,6,7類型,其中最5類型是集合類型,因爲不知道集合類型中的具體類型(由於存在繼承關係),因此類型無關。對於第8,9類型,其中第8類型爲對象數組類型,對於對象數組,數組中的每個對象的類型都是肯定的,且整個數組只有一種類型,所以能夠肯定其類型,這時候就要使用類型相關解析器了,對於第9類型,須要使用解析對象的類型來肯定相對應的javaBean屬性,所以是類型相關。
另一個肯定解析器的過程中,使用了映射機制,即將當前解析器與對應的類型映射起來,以便下一次時使用。對於集合類型及子類型,因爲當前類型並非肯定的List或Collection類型,所以將當前類型與集合解析器映射起來。對於對象類型,須要將當前類型傳遞給相對應的解析器,以肯定具體的屬性。設計模式
解析過程
解析方法由統一的接口所定義,每一個不一樣的解析器只須要實際這個方法並提供各自的實現便可。此方法爲write(JSONSerializer serializer, Object object),由ObjectSerializer提供。帶兩個參數,第一個參數,即爲解析的起點類jsonSerializer,此類封裝了咱們所須要的outWriter類,須要時只須要今後類取出便可。第二個參數爲咱們所要解析的對象,在各個子類進行實現時,可將此對象經過強制類型轉換,轉換爲所須要的類型便可。
具體的解析過程根據不一樣的數據類型不所不一樣,對於第1,2類型,因爲在outWriter中均有相對應的方法,因此在具體實現時,只須要調用相應的outWriter方法便可,而不須要做額外的工做。好比對於字符串解析器,它的解析過程以下所示:數組
即首先,取得輸出時所須要的outWriter,而後再根據配置決定是輸出單引號+字符串仍是雙引號+字符串。緩存
而對於其它並無由outWriter所直接支持的類型而言,解析過程就相對於複雜一些。可是總的邏輯仍是基於如下邏輯:性能優化
只需此三步,便可完美的解析一個對象。所以,咱們能夠參考其中的一個具體實現,並討論其中的具體優化措施。
在取得解析器方法getObjectWriter(Class<?> clazz)中,咱們能夠看到,對於集合類型中的Collection和List,fastjson是分開進行解析的。即二者在解析時在細微處有着不同的實現。二者的區別在於List是有序的,能夠根據下標對元素進行訪問,對於經常使用List實現,ArrayList,使用下標訪問子元素的價格爲O1。這就是在fastJson中採起的一點優化措施,詳細看如下實現代碼:app
如下實現與collection相比不一樣的即在於處理中間元素與末尾元素的區別。相對於Collection,就不能使用以上的方法了,在實現上,就只能先輸出前綴,再一個一個地處理裏面的元素,最後輸出後綴。此實現以下所示:jvm
以上代碼就是一般最多見的實現了。
相對於集合類型實現,map實現和javaBean實現相對來講,稍微複雜了一點。主要是輸出key和value的問題。在fastjson中,key輸出表現爲使用outWriter的writeKey來進行輸出,value輸出則一樣使用常規的輸出。按照咱們先前所說的邏輯,首先仍是輸出包裝字符內容,即{,在末尾輸出}。而後,再根據每一個key-value映射特色,採起相對應的輸出方式。
固然,對於map類型輸出和javaBean輸出仍是不同的。二者能夠互相轉換,但fastjson在輸出時仍是採起了不同的輸出方式。那麼,咱們以源代碼來查看相應的實現:
由上能夠看出,map的實現仍是按照咱們經常使用的思路在進行。在性能優化處,採起了兩個優化點,一是輸出字段時,並不直接輸出字段名,而是採起字段+冒號的方式一塊兒輸出,減小outWriter擴容計算。二是在查找value解析器時,儘可能使用前一個value的解析器,避免重複查找。
相比map,javaBean的實現就相對更復雜。javaBean輸出並非採起key-value的方式,而是採起相似fieldSerializer的輸出方式,即將屬性名和值,組合成一個字段,一塊兒進行輸出。固然輸出時仍是先輸出字段名,再輸出值的實現。那麼對於javaBean實現,首先要取得當前對象類型的全部能夠輸出的類型。
在fastjson實現中,並無採起javaBean屬性的讀取方式,而是採起了使用getXXX和isXXX方法的讀取模式,來取得一個類型的可讀取屬性。做爲性能優化的一部分,讀取屬性的操做結果被緩存到了getter緩存器中。其實,並非緩存到了getter緩存器中,只是該類型的javaBean序列化器對象被緩存到了jsonSerializer的對象類型-序列化器映射中。對於同一個類型,就不須要再次解析該類型的屬性了。
有了相對應的字段,那麼在實現時,就按照相應的字段進行輸出便可。如下爲實現代碼:
由上可見,javaBean的輸出實際上和map輸出差很少。只不過這裏又把屬性的解析和輸出封裝了一層。在使用字段解析器(由FieldSerializer標識)輸出字段值時,實際上也是先輸出字段名+冒號,再輸出字段值。這裏就再也不詳細敘述。
總結
在整個解析過程當中,更多的是根據對象類型查找到對象解析器,再使用對象解析器序列化對象的過程。在這中間,根據不一樣的對象採起不一樣的解析,並在實現中採起部分優化措施,以儘可能地提升解析效率,減小中間運算。減小中間運算,是在解析過程當中採起的最主要的優化辦法。實際上,最主要的優化措施仍是體如今outWriter中對於數據的處理上。此處更多的是設計模式的使用,以實現繁多的對象的解析。 整個fastjson的序列化部分,就到此爲止。單就筆者而言,在查看源代碼的時候,也發現了一些問題,多是做者未考慮的問題,或者是實際中未遇到。但在版本升級過程當中,也漸漸地對功能進行了加強,好比對於@JsonField註解的使用,NameFilter和ValueFilter的使用,使fastjson愈來愈符合業務系統的須要。若是能夠,筆者會將其用到筆者所在的項目中,而再也不重複發明輪子:)