簡體   English   中英

如何處理包更改的Java序列化對象?

[英]How to deal with a Java serialized object whose package changed?

我有一個Java類存儲在HttpSession對象中,該對象在集群環境中的服務器之間進行序列化和傳輸。 出於此解釋的目的,我們將此類稱為“Person”。

在改進代碼的過程中,這個類從“com.acme.Person”轉移到“com.acme.entity.Person”。 在內部,類保持完全相同(相同的字段,相同的方法,相同的一切)。

問題是我們有兩組服務器同時運行舊代碼和新代碼。 具有舊代碼的服務器具有序列化的HttpSession對象,並且當新代碼反序列化它時,它會拋出ClassNotFoundException,因為它找不到對com.acme.Person的舊引用。 此時,處理這個很容易,因為我們可以使用新包重新創建對象。 然后問題變成新服務器中的HttpSession將使用對com.acme.entity.Person的新引用來序列化對象,並且當在運行舊代碼的服務器中對其進行反序列化時,將拋出另一個異常。 此時,我們再也無法處理此異常。

對於這類案件,最好的策略是什么? 有沒有辦法告訴新服務器通過引用舊包序列化對象並將舊包的引用反序列化為新包? 所有服務器運行新代碼后,我們將如何過渡到使用新包並忘記舊包?

我發現這篇博文聲稱有一個解決方案,但它並沒有非常清楚地說明。

它實際上說的是你創建一個ObjectInputStream的子類來覆蓋readClassDescriptor方法來做這樣的事情:

@Override
protected java.io.ObjectStreamClass readClassDescriptor() 
        throws IOException, ClassNotFoundException {
    ObjectStreamClass desc = super.readClassDescriptor();
    if (desc.getName().equals("oldpkg.Widget")) {
        return ObjectStreamClass.lookup(newpkg.Widget.class);
    }
    return desc;
};

您還應該看看這個問題及其答案 ,其中包含與您的問題相同的一些問題。

我的建議是: 支持舊版本軟件讀取新版本序列化數據的情況。

  • 這是鼓勵(實際上強迫)人們升級到最新版本的代碼庫的好機會。 一般來說,這種情況發生在所有人的利益之中,這種情況很快就會發生。

  • 如果由於其他原因迫使人們升級為時過早,那么(IMO)您應該認真考慮撤銷對類/包名稱的更改。 等到你有一個明確的升級戰略/計划,1)技術上合理,2)所有利益相關者都能接受。

這對Java序列化來說總是很頭疼。 只要你正在遷移你的類,我建議你考慮遷移到不同的序列化機制,比如XStream。 在JavaLobby 有一篇有趣的文章

如果您樂意宣傳您的更改,那么您就會被搞砸,反彈舊服務器。 任何實現目標的黑客攻擊只會增加更多的垃圾代碼,並且會造成更多混亂和麻煩。

在某些情況下,您無權訪問ObjectInputStream,也無法覆蓋readClassDescriptor() 例如,另一個子系統序列化和反序列化這些對象。 在我們的例子中,我們在Quartz作業數據映射中序列化了舊實例。

對於這種情況,您必須維護舊類的淺層定義。 您可以實現舊類的readResolve()writeReplace()方法。

class OldClass{
  private int aField;
  private Object readResolve() throws ObjectStreamException {
     return new NewClass(aField);
  }
  private Object writeReplace() throws ObjectStreamException {
     return new NewClass(aField);
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM