Rendere Java runtime ignora serialVersionUIDs?

Devo lavorare con un gran numero di classi Java compilate che non hanno specificato esplicitamente un serialVersionUID. Poiché i loro UID sono stati generati arbitrariamente dal compilatore, molte delle classi che devono essere serializzate e deserializzate finiscono per causare eccezioni, anche se le attuali definizioni di class coincidono. (Questo è tutto il comportamento previsto, ovviamente.)

Non è pratico per me tornare indietro e correggere tutto questo codice di terze parti.

Pertanto, la mia domanda è: c’è un modo per far sì che Java runtime ignori le differenze in serialVersionUIDs, e fallisca la deserializzazione solo quando ci sono reali differenze nella struttura?

    Se si ha accesso al codice base, è ansible utilizzare l’ attività SerialVer per Ant per inserire e modificare il serialVersionUID nel codice sorgente di una class serializzabile e risolvere il problema una volta per tutte.

    Se non è ansible o se questa non è un’opzione (ad esempio se sono già stati serializzati alcuni oggetti che è necessario deserializzare), una soluzione potrebbe essere quella di estendere ObjectInputStream . Aumentare il suo comportamento per confrontare il serialVersionUID del descrittore di stream con serialVersionUID della class nella JVM locale che questo descrittore rappresenta e utilizzare il descrittore di class locale in caso di mancata corrispondenza. Quindi, usa questa class personalizzata per la deserializzazione. Qualcosa di simile (crediti a questo messaggio ):

     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; } } 

    Quanto è poco pratico da risolvere? Se si ha il sorgente e si può ribuild, non si può semplicemente eseguire uno script sull’intera base di codice per inserire a

     private long serialVersionUID = 1L; 

    ovunque ?

    Usa CGLIB per inserirli nelle classi binarie?

    Gli errori di serializzazione in fase di esecuzione indicano in modo esplicito quale sarà l’ID previsto. Basta cambiare le tue classi per dichiararle come ID e tutto andrà bene. Questo comporta che tu faccia dei cambiamenti ma non credo che ciò possa essere evitato

    È ansible utilizzare Aspectj per “introdurre” il campo in ogni class serializzabile durante il caricamento. Vorrei prima introdurre un’interfaccia marker in ogni class usando il pacchetto e poi introdurre il campo usando un hash del file di class per serialVersionUID

     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; } } 

    Sarà necessario aggiungere l’ agent del tempo di caricamento aspectj alla VM in modo che possa intrecciare il consiglio alle classi di terze parti esistenti. È divertente anche se una volta che hai deciso di impostare Aspectj, è notevole il numero di usi che dovrai utilizzare.

    HTH

    ste