Hacer que el tiempo de ejecución de Java ignore los ID de serialVersion?

Tengo que trabajar con una gran cantidad de clases comstackdas de Java que no especificaron explícitamente un serialVersionUID. Debido a que sus UIDs fueron generados arbitrariamente por el comstackdor, muchas de las clases que necesitan ser serializadas y deserializadas terminan causando excepciones, a pesar de que las definiciones de clase reales coinciden. (Esto es todo el comportamiento esperado, por supuesto).

No es práctico para mí volver atrás y corregir todo este código de terceros.

Por lo tanto, mi pregunta es: ¿hay alguna manera de hacer que Java runtime ignore las diferencias en serialVersionUIDs, y solo no se deserialice cuando haya diferencias reales en la estructura?

Si tiene acceso a la base de código, puede usar la tarea SerialVer para Ant para insertar y modificar el serialVersionUID en el código fuente de una clase serializable y solucionar el problema de una vez por todas.

Si no puede, o si esta no es una opción (por ejemplo, si ya ha serializado algunos objetos que necesita deserializar), una solución sería extender ObjectInputStream . Aumente su comportamiento para comparar el serialVersionUID del descriptor de flujo con el serialVersionUID de la clase en la JVM local que representa este descriptor y para usar el descriptor de clase local en caso de discrepancia. Entonces, solo usa esta clase personalizada para la deserialización. Algo así (créditos a este mensaje ):

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

¿Qué tan poco práctico es arreglar esto? Si tiene la fuente y puede reconstruir, ¿no puede simplemente ejecutar una secuencia de comandos en toda la base de código para insertar un

 private long serialVersionUID = 1L; 

en todos lados ?

Use CGLIB para insertarlos en las clases binarias?

Los errores de serialización en el tiempo de ejecución le dicen explícitamente qué se espera que sea el ID. Simplemente cambie sus clases para declararlas como ID y todo estará bien. Esto implica que hagas cambios, pero no creo que esto pueda evitarse

Posiblemente podría utilizar Aspectj para ‘presentar’ el campo en cada clase serializable a medida que se carga. Primero introduciría una interfaz de marcador en cada clase usando el paquete y luego presentaría el campo usando un hash del archivo de clase para el 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; } } 

Necesitará agregar el agente tejedor de tiempo de carga aspectj a la VM para que pueda tejer el consejo en sus clases de terceros existentes. Aunque es divertido una vez que te preparas para configurar Aspectj, es notable la cantidad de usos que pondrás.

HTH

ste