java 自定义注解 生成json_用自定义注解实现fastjson序列化的扩展
這篇文章起源于項目中一個特殊的需求。由于目前的開發方式是前后端分離的,基本上是通過接口提供各個服務。
而前兩天前端fe在開發中遇到了一些問題:他們在處理字符串類型的時間時會出現精度丟失的情況,所以希望后臺是以時間戳的形式返回給前端。而與此同時后臺的設計是這個樣子的:所有的時間在數據庫中均保存為varchar類型,在序列化的時候也是按String字符串去處理的。
這樣一來就需要一些解決方案:
1. 所有數據庫的時間字段都用timestamp替換,這個是最簡單確實實現代價最高的一種方案,由于數據庫表太多并且涉及多處的耦合,此方案不可行。
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還真的厲害啊。作為一個學習者,只是希望有朝一日也能寫出這么漂亮的代碼吧,希望自己可以不斷努力!
總結
以上是生活随笔為你收集整理的java 自定义注解 生成json_用自定义注解实现fastjson序列化的扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue 日期格式化返回指定个数月份_12
- 下一篇: matlab非同秩矩阵相乘_线性代数精华