让 Java 运行时忽略 serialVersionUIDs?

2022-09-02 02:52:46

我必须使用大量编译的Java类,这些类没有显式指定串行VersionUID。由于它们的 UID 是由编译器任意生成的,因此许多需要序列化和反序列化的类最终会导致异常,即使实际的类定义匹配也是如此。(当然,这都是预期的行为。

对我来说,返回并修复所有这些第三方代码是不切实际的。

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


答案 1

如果您有权访问代码库,则可以使用 Ant 的 SerialVer 任务插入和修改可序列化类的源代码,并一劳永逸地修复问题。serialVersionUID

如果不能,或者如果这不是一个选项(例如,如果您已经序列化了一些需要反序列化的对象),一种解决方案是扩展 。扩充其行为,以将流描述符与此描述符表示的本地 JVM 中的类进行比较,并在不匹配时使用本地类描述符。然后,只需使用此自定义类进行反序列化。像这样的东西(归功于这条消息):ObjectInputStreamserialVersionUIDserialVersionUID

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);
    }
    
    @Override
    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;
    }
}

答案 2

使用 CGLIB 将它们插入到二元类中?