使用 readClassDescriptor() 和 resolveClass() 来允许序列化版本控制 [英] Using readClassDescriptor() and maybe resolveClass() to permit Serialization versioning

查看:16
本文介绍了使用 readClassDescriptor() 和 resolveClass() 来允许序列化版本控制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究 Java 序列化机制中的不同选项,以便在我们的类结构中灵活地进行版本容忍存储(并且提倡使用不同的机制,您无需告诉我).

I am investigating different options in the Java Serialization mechanism to allow flexibility in our class structures for version-tolerant storage (and advocating for a different mechanism, you don't need to tell me).

例如,如果只需要向后兼容,默认序列化机制可以处理添加和删除字段.

For instance, the default serialization mechanism can handle both adding and removing fields, if only backwards compatibility is required.

事实证明,重命名类或将其移动到不同的包要困难得多.我在 这个问题 我能够通过继承 ObjectInputStream 和覆盖 readClassDescriptor() 来做一个简单的类重命名和/或移动包:

Renaming a class or moving it to a different package has proved to be much harder, though. I found in this question that I was able to do a simple class rename and/or move package, by subclassing ObjectInputStream and overriding readClassDescriptor():

    if (resultClassDescriptor.getName().equals("package.OldClass"))
        resultClassDescriptor = ObjectStreamClass.lookup(newpackage.NewClass.class);

这适用于简单的重命名.但是,如果您随后尝试添加或删除字段,则会收到 java.io.StreamCorruptedException.更糟糕的是,即使添加或删除了一个字段,也会发生这种情况,然后重命名该类,这可能会导致多个开发人员或多个签入出现问题.

That is fine for simple renames. But if you then try to add or delete a field, you get a java.io.StreamCorruptedException. Worse, this happens even if a field has been added or deleted, and then you rename the class, which could cause problems with multiple developers or multiple checkins.

基于我所做的一些阅读,我尝试了一些也覆盖resolveClass()的想法,我们正确地将名称重新指向新类,但不加载旧类本身并轰炸字段更改.但这来自于对序列化机制的一些细节的非常模糊的理解,我什至不确定我是否在咆哮正确的树.

Based on some reading I had done, I experimented a bit with also overriding resolveClass(), with the idea that we were correctly repointing the name to the new class, but not loading the old class itself and bombing on the field changes. But this comes from a very vague understanding of some the details of the Serialization mechanism, and I'm not sure if I'm even barking up the right tree.

所以 2 个精确的问题:

So 2 precise questions:

  1. 为什么使用 readClassDescriptor() 重新指向类名会导致反序列化在正常、兼容的类更改时失败?
  2. 有没有办法使用 resolveClass() 或其他机制来绕过这并允许类发展(添加和删除字段)并成为重命名/重新打包?

我四处寻找,找不到关于 SO 的等效问题.无论如何,如果存在这样的问题,请向我指出这样的问题,但请仔细阅读该问题,不要关闭我,除非另一个问题实际上回答了我的确切问题.

I poked around and could not find an equivalent question on SO. By all means, point me to such a question if it exists, but please read the question carefully enough that you do not close me unless another question actually answers my precise question.

推荐答案

我遇到了和你一样的灵活性问题,我找到了方法.所以这里是我的 readClassDescriptor() 版本

I had same problems with flexibility like you and I found the way. So here my version of readClassDescriptor()

    static class HackedObjectInputStream extends ObjectInputStream
{

    /**
     * Migration table. Holds old to new classes representation.
     */
    private static final Map<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>();

    static
    {
        MIGRATION_MAP.put("DBOBHandler", com.foo.valueobjects.BoardHandler.class);
        MIGRATION_MAP.put("DBEndHandler", com.foo.valueobjects.EndHandler.class);
        MIGRATION_MAP.put("DBStartHandler", com.foo.valueobjects.StartHandler.class);
    }

    /**
     * Constructor.
     * @param stream input stream
     * @throws IOException if io error
     */
    public HackedObjectInputStream(final InputStream stream) throws IOException
    {
        super(stream);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
    {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

        for (final String oldName : MIGRATION_MAP.keySet())
        {
            if (resultClassDescriptor.getName().equals(oldName))
            {
                String replacement = MIGRATION_MAP.get(oldName).getName();

                try
                {
                    Field f = resultClassDescriptor.getClass().getDeclaredField("name");
                    f.setAccessible(true);
                    f.set(resultClassDescriptor, replacement);
                }
                catch (Exception e)
                {
                    LOGGER.severe("Error while replacing class name." + e.getMessage());
                }

            }
        }

        return resultClassDescriptor;
    }

这篇关于使用 readClassDescriptor() 和 resolveClass() 来允许序列化版本控制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆