2009-11-29 14 views
12

Açıkça bir serialVersionUID belirtmeyen çok sayıda derlenmiş Java sınıfı ile çalışmam gerekiyor. UID'leri derleyici tarafından keyfi olarak oluşturulduğundan, seri hale getirilmesi ve serileştirilmesi gereken sınıfların çoğu, gerçek sınıf tanımları eşleşmesine rağmen istisnalara neden olur. (Elbette beklenen tüm davranışlar.)Java çalışma zamanını serialVersionUID'leri dikkate almayın?

Bu üçüncü tarafa ait tüm kodları geri dönüp düzeltmem mümkün değildir.

Dolayısıyla sorum şu: Java çalışma zamanını serialVersionUIDs içinde farklılıkları görmezden ve sadece yapısında Gerçek farklar varken serisini başarısız kılmak için herhangi bir yolu var mı?

cevap

27

önlenebilir inanmıyoruz ve sorunu hep bir kez düzeltin.

Yapamazsanız veya bu bir seçenek değilse (örneğin, seri hale getirmeniz gereken bazı nesneleri önceden serileştirdiyseniz), bir çözüm ObjectInputStream'u uzatmak olacaktır. Akış tanımlayıcısının serialVersionUID akış tanımlayıcısının sınıfını, bu tanımlayıcının temsil ettiği yerel JVM'deki sınıfın serialVersionUID ile karşılaştırmasını ve uyumsuzluk durumunda yerel sınıf tanımlayıcısını kullanmasını sağlayın. Ardından, seri hale getirme için bu özel sınıfı kullanın. Böyle bir şey (this message için kredi):

import java.io.IOException; 
import java.io.InputStream; 
import java.io.InvalidClassException; 
import java.io.ObjectInputStream; 
import java.io.ObjectStreamClass; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 


public class DecompressibleInputStream extends ObjectInputStream { 

    private static Logger logger = LoggerFactory.getLogger(DecompressibleInputStream.class); 

    public DecompressibleInputStream(InputStream in) throws IOException { 
     super(in); 
    } 

    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { 
     ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor 
     Class localClass; // the class in the local JVM that this descriptor represents. 
     try { 
      localClass = Class.forName(resultClassDescriptor.getName()); 
     } catch (ClassNotFoundException e) { 
      logger.error("No local class for " + resultClassDescriptor.getName(), e); 
      return resultClassDescriptor; 
     } 
     ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass); 
     if (localClassDescriptor != null) { // only if class implements serializable 
      final long localSUID = localClassDescriptor.getSerialVersionUID(); 
      final long streamSUID = resultClassDescriptor.getSerialVersionUID(); 
      if (streamSUID != localSUID) { // check for serialVersionUID mismatch. 
       final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: "); 
       s.append("local serialVersionUID = ").append(localSUID); 
       s.append(" stream serialVersionUID = ").append(streamSUID); 
       Exception e = new InvalidClassException(s.toString()); 
       logger.error("Potentially Fatal Deserialization Operation.", e); 
       resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization 
      } 
     } 
     return resultClassDescriptor; 
    } 
} 
+1

Bu kod günlerimi kurtardı! Çok teşekkürler! – peceps

+0

İkinci mesaj bağlantısı bozuk mu? (altı yıl sonra, çok şaşırmadım, akıl) –

+0

Çok teşekkürler. Bu snippet aslında benim için de günü kurtardı. – nserror

2

Bunun düzeltilmesi ne kadar pratik? Eğer kaynağına sahip ve yeniden olabilir varsa, sadece her yerde bir

private long serialVersionUID = 1L; 

eklemek için tüm kod temeli üzerinde bir komut dosyası çalıştırabilirsiniz?

2

CGLIB'yi bunları ikili sınıflara eklemek için kullanın.

+0

İyi fikir ama ... ne tür bir değer tam olarak ekleyebilirim? Serileştirilmiş sürümün serialVersionUID'sini okumanız gerekir. Bu zor kısmı. –

+0

Oh, whoops. Evet, orada ne olduğunu bilmelisin. – bmargulies

1

Çalışma zamanındaki serileştirme hataları, kimliğin ne olması gerektiğini açıkça belirtir. Sadece kimliklerini bu kimliği olarak ilan etmek için değiştirin ve her şey yoluna girecek. Bu değişiklikler yaparak içeriyor ama bu kod tabanına erişiminiz varsa, eklemek SerialVer task for Ant kullanabilir ve bir seri hale getirilebilir sınıfın kaynak kodunda serialVersionUID değiştirmeye

0

Muhtemelen o yüklendiği her seri hale getirilebilir sınıfa alanını 'tanıtmak' için AspectJ'yi kullanabilirsiniz. Ben ilk paketi kullanarak her sınıfa bir işaretleyici arabirimini tanıtmak ve daha sonra serialVersionUID için

public aspect SerializationIntroducerAspect { 

    // introduce marker into each class in the org.simple package 
    declare parents: (org.simple.*) implements SerialIdIntroduced; 

    public interface SerialIdIntroduced{} 

    // add the field to each class marked with the interface above. 
    private long SerialIdIntroduced.serialVersionUID = createIdFromHash(); 

    private long SerialIdIntroduced.createIdFromHash() 
    { 
     if(serialVersionUID == 0) 
     { 
      serialVersionUID = getClass().hashCode(); 
     } 
     return serialVersionUID; 
    } 
} 

Bunu örgü böylece VM aspectj load time weaver agent eklemeniz gerekecektir sınıf dosyasının bir karma kullanarak alanını getirecek Mevcut 3. parti sınıflarına tavsiyede bulunun. Komik bir kez olsun Aspectj ayarlayarak olsun, onun koymak için kullanacağınız kullanımların sayısı dikkat çekici.

HTH

ste

+0

Bu iyi bir fikir ama bu yeterli değil. Sorun bir serialVersionUID eklemek değil, sorun serileştirilmiş sürümde olduğu gibi aynı serialVersionUID eklemektir. –

İlgili konular