Hessian序列化bug分析

跟hessian的Ref機(jī)制有關(guān),對于Timestamp對象,解析器和反解析器在Ref上有不一致的地方導(dǎo)致。

具體分析如下:

序列化器類圖

SerializeFactory來管理這些序列化類,它們都是AbstractSerializer的子類,實(shí)現(xiàn)共同的接口void writeObject(Object obj, AbstractHessianOutput out)?;工廠根據(jù)對象類型來創(chuàng)建不同的序列化類,BasicSerializer是針對基本類型包括包裝類型,JavaSerializer是自定義對象類型,SqlDateSerializer則是針對java.sql.Date?、java.sql.Time、java.sql.Timestamp類型的,其他的都是顧名思義。這里有一個關(guān)鍵點(diǎn),那就是java.util.Date類型的是放在BasicSerializer中的.

Hessian序列化很快的一個原因是Ref機(jī)制,這里我們對它詳細(xì)地描述下。我們會發(fā)現(xiàn)大多數(shù)序列化類開始之前都有這樣一段代碼:

這段代碼的主要作用就是如果map里有與該對象內(nèi)存地址相同的,我們就不序列化該對象本身,只序列化它的引用。具體如下:

_refs是hessian實(shí)現(xiàn)的一個類似hashmap的數(shù)據(jù)結(jié)構(gòu),有自己的散列和rehash。key是被序列化對象,value是加入時_refs的的大小。get是通過判斷內(nèi)存地址來比較的。如果找到一樣的,就序列化它的引用,這是序列化流里會有個標(biāo)識為r以及加入_refs時的順序ref,如果找不到,就把它放到這個map里。

反序列化時,讀取map,遍歷,對key和value分別反解析并放到map中,中間采用了大量的遞歸調(diào)用。本地維護(hù)了一個ArrayList :_refs(同名不同的數(shù)據(jù)結(jié)構(gòu)),每一個對象在被反解析時(基本、包裝類型除外),會將自己本身加入到該list中。讀到對象類型為r時,會從_refs里直接獲取該對象,get(index),index為序列化時寫入的ref,直接將字段set為獲取的對象。

hessian并沒有在序列化時將_refs表通過網(wǎng)絡(luò)傳過去,而是通過同樣的解析和反解析順序,保證了兩邊從_refs里獲取對象的一致性。那么,它就要保證添加Ref和讀取Ref的邏輯是一致的,如果不一致就會出現(xiàn)類型不一致的異常。

那么我們的service為什么會出錯呢?因?yàn)閔essian在序列化時Timestamp是走的SqlDateSerializer,這個序列化器是會加入到_refs這個map里的,但是讀取的時候,Timestamp卻不會加入到客戶端里地_refs表里。這時候我們兩端_refs就不一致了。而phoneList在數(shù)據(jù)庫phone字段為空時都會賦為ArrayUtils.EMPTY_STRING_ARRAY,它是一個final static對象。String[]是會寫_refs表的,如果有第二個String[]跟第一個相等,那么它會被標(biāo)記為引用類型并記下加入表時的順序。這時候反序列化的時候timestamp是不會加入_refs的,反序列化出來String[]的ref(即list中的index)就有偏差了。java.util.Date是被視為基礎(chǔ)類型的,服務(wù)和客戶端都不會寫入_refs表。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容