使Java运行时忽略serialVersionUIDs?

我必须处理大量已编译的Java类,这些类没有明确指定serialVersionUID。 因为它们的UID是由编译器任意生成的,所以许多需要序列化和反序列化的类最终导致exception,即使实际的类定义匹配。 (当然,这是所有预期的行为。)

我回过头来修复所有这些第三方代码是不切实际的。

因此,我的问题是:有没有办法让Java运行时忽略 serialVersionUID中的差异,并且只有在结构存在实际差异时才能反序列化?

如果您有权访问代码库,则可以使用Ant的SerialVer任务来插入和修改可序列化类的源代码中的serialVersionUID ,并为所有人解决问题。

如果你不能,或者这不是一个选项(例如,如果你已经序列化了一些需要反序列化的对象),一个解决方案就是扩展ObjectInputStream 。 增加其行为以将流描述符的serialVersionUID与此描述符所表示的本地JVM中的类的serialVersionUID进行比较,并在不匹配的情况下使用本地类描述符。 然后,只需使用此自定义类进行反序列化。 像这样的东西(信息这个消息 ):

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

这是多么不切实际的修复? 如果您有源并且可以重建,那么您是否可以在整个代码库上运行脚本来插入

 private long serialVersionUID = 1L; 

无处不在

使用CGLIB将它们插入二进制类?

运行时的序列化错误会明确告诉您ID应该是什么。 只需更改您的类以将其声明为ID,一切都会好的。 这确实涉及到您进行更改但我不相信这可以避免

您可以使用Aspectj在加载时将字段“引入”每个可序列化类。 我首先使用包在每个类中引入一个标记接口,然后使用类文件的哈希为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; } } 

您需要将aspectj加载时weaver代理添加到VM,以便它可以将建议编织到现有的第3方类中。 它很有趣,虽然一旦你开始设置Aspectj,它将显着的用途数量。

HTH

STE