這篇文章起源於項目中一個特殊的需求。因爲目前的開發方式是先後端分離的,基本上是經過接口提供各個服務。前端
而前兩天前端fe在開發中遇到了一些問題:他們在處理字符串類型的時間時會出現精度丟失的狀況,因此但願後臺是以時間戳的形式返回給前端。而與此同時後臺的設計是這個樣子的:全部的時間在數據庫中均保存爲varchar類型,在序列化的時候也是按String字符串去處理的。java
這樣一來就須要一些解決方案:
數據庫
1. 全部數據庫的時間字段都用timestamp替換,這個是最簡單確實實現代價最高的一種方案,因爲數據庫表太多而且涉及多處的耦合,此方案不可行。json
2. 經過fastjson序列化層去轉換類型:首先想到的是能不能經過fastjson本身提供的註解方式實現,後調研以後發現,目前使用的版本並不支持;而後想到能夠經過fastjson提供的序列化器重載實現String類型的攔截並作處理。可是這種方案會把全部String的字段都執行這段邏輯,而且還要經過固定的format肯定哪些是日期,這裏很是影響性能;最後決定添加自定義註解,在須要這種類型轉換的字段上加上自定義的@StringToDate註解,而後在序列化執行的入口,給全部加了此註解的類提供繼承並實現擴展的序列化器,完成自定義的序列化過程。後端
下面就詳細說一下這個過程,一是對fastjson源碼作一個解析和說明,二也是對本身的工做作一個總結。緩存
1. 如何實現擴展前後端分離
首先來看一下自定義的註解,很簡單:工具
關於這個註解的用法和定義,我就不細說了,Retention指定了他的做用域,而Target說明該註解用於field字段上。性能
而後既然是要擴展fastjson的功能,咱們就來看一下fastjson的入口,首先是WebMvcConfigurerAdapter這個類,咱們能夠經過繼承和重寫該類的方法實現MVC的配置,好比攔截器、資源處理器等。咱們重寫的方法是configureMessageConverters,這個是用來配置信息轉化的converter:學習
能夠看到,咱們經過這個方法,調用了父類,同時加入了fastJson的converter,而後咱們來看看FastJsonMessageConverter這個咱們本身寫的類:
只是很簡單的經過繼承實現了自定義config的注入,而後再來看看咱們本身寫的這個config:
這裏是最關鍵的地方,SerializeConfig提供一個入口方法getObjectWriter能夠用來對傳入的類型進行處理,併爲相應的類型設置對應的Serializer序列化器。這裏能夠看到,我經過判斷傳入class的註解判斷是否是要處理的類型,若是是須要轉換的,就用咱們寫的ExtendJavaBeanSerializer去處理他。(同時能夠看到,這裏我作了緩存處理,每次的class在處理以後都會經過父類的put方法放入緩存,這樣能夠大大減小遍歷和判斷的次數,提升處理性能)
那如今就要看看這個ExtendJavaBeanSerializer作了什麼:
這裏咱們繼承了JavaBeanSerializer並重寫了processValue方法。這裏要說個點:由於每一個序列化器都有write方法,因此最開始直觀的想法是重寫這個方法實現擴展,可是因爲write方法很長很長(有250多行),做爲切入點很是不方便,而後仔細觀察源碼,發現其中有這麼一句:
propertyValue = this.processValue(serializer, fieldSerializer.fieldContext, object, fieldInfoName,propertyValue);
這句看上去好像就是給咱們處理value值的,再點進去一看,processValue竟然是個protected方法。那正如意!這就是給咱們作擴展的入口,最後便重寫了這個方法,並在其中實現了StringToDate的轉換邏輯。
2. fastjson源碼思想
其實呢,到這裏應該會有一個疑問,fastjson提供的預設序列化器有不少不少,看源碼發現一個包下面滿滿都是。那麼咱們是如何肯定要繼承這個JavaBeanSerializer的呢?這就須要看fastjson的源碼了,看看它到底是怎麼分配和執行這個序列化器的:
首先是SerializeConfig這個類:這個類的config中預設了大量的類型和序列化器的對應關係,他原有的getObjectWriter入口是這樣的邏輯: 先判斷傳入類是否是config中有的,若是有直接給對應的Serializer,若是沒有,又會有一堆特殊類(好比集合,異常,編碼等類)的判斷,而他們也有對應的Serializer。
那若是尚未(好比本身的javabean),在最後會執行一個:createJavaBeanSerializer(clazz) 的方法,在這個方法中,默認會執行createASMSerializer方法,asm查閱資料會發現是一個老外寫的字節碼序列化器,是序列化最底層的一個工具,不少開源序列化包都是對這個進行封裝實現的。那咱們要繼承JavaBeanSerializer這個類並使用本身的,就說明咱們不想讓他執行createASMSerializer這個方法,爲何呢?
由於在createASMSerializer裏面的邏輯是:當序列化對象 > 256個時,會建立可繼承的JavaBeanSerializer去處理,若是<256,則不會給他任何序列化器,而是直接經過反射方式在內存中(就是生生拼出了一個序列化過程)實現對這個對象的處理。關鍵代碼以下:
固然,下面還有很長很長的內存字節碼操做的邏輯,我就不貼出了。我想這可能也是fastjson快的一個緣由吧。
寫到這裏,關於對fastjson的擴展就差很少說完了,其實fastjson的源碼中其餘類中也都有本身獨特的優化方式,有些仍是挺有意思的。另外呢,我驚奇地發現,fastjson的做者和durid竟然是一我的,這個來自阿里的wenshao還真的厲害啊。做爲一個學習者,只是但願有朝一日也能寫出這麼漂亮的代碼吧,但願本身能夠不斷努力!