当前位置: 首页 > 面试题库 >

使用readClassDescriptor()以及resolveClass()允许序列化版本控制

白晋鹏
2023-03-14
问题内容

我正在研究Java序列化机制中的不同选项,以允许我们的类结构具有灵活性以实现版本允许的存储(并且提倡使用其他机制,您无需告诉我)。

例如,如果仅要求向后兼容,则默认的序列化机制可以处理添加和删除字段。

不过,事实证明,重命名一个类或将其移至其他程序包要困难得多。在这个问题中,我发现可以通过子类化ObjectInputStream并重写readClassDescriptor()来进行简单的类重命名和/或移动包:

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

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

根据我所做的一些阅读,我做了一些尝试,也重写了resolveClass(),以为我们可以正确地将名称重新指向新类,而不是加载旧类本身,也不会对字段更改进行轰炸。但这来自对序列化机制的一些细节的模糊理解,我不确定我是否在狂吠正确的树。

所以2个精确的问题:

  1. 为什么使用readClassDescriptor()重指向类名会导致反序列化在常规的兼容类更改时失败?
  2. 有没有一种方法可以使用resolveClass()或其他机制解决此问题,并允许类演化(添加和删除字段)并重命名/重新打包?

我四处张望,找不到关于SO的等效问题。一定要把我指向这样一个问题(如果存在),但请仔细阅读该问题,除非您有另一个问题确实回答了我的确切问题,否则请不要与我联系。


问题答案:

像您一样,我在灵活性方面也遇到了同样的问题,我找到了解决方法。所以这是我的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;
    }


 类似资料:
  • 问题内容: 所以我不确定到底要给大家看什么,但是如果您需要更多代码,请随时询问: 因此,此方法将在我们的应用程序中为Zend设置initMailer: 您可以看到其中的闭包。当我运行使用此代码的任何测试时,都会得到: 因此,与此“关闭”相关的所有测试均失败。所以我在这里问你们我应该怎么做。 为了澄清上述内容,所有操作都是在说,我们发出的任何电子邮件都希望将有关该电子邮件的信息存储在文件/ tmp

  • 我正在尝试缓存一个网站的站点地图,它是从我正在工作的一个网站的控制器生成的,但显然我做错了什么,因为我不理解错误消息。下面是造成问题的代码片段(它是一个控制器方法)。在添加缓存之前,一切都正常工作。

  • 相对来说,我对Ajax还比较陌生,只是负责这个跨域调用。我们的网页上有一个文本框,用户将使用它来执行公司名称的搜索。通过单击文本框旁边的按钮,将请求Ajax调用。不幸的是,web服务位于一个单独的域中,所以这自然会引起问题。 以下是我为完成这项工作所做的最大努力。我还应该注意,这个调用的目的是以XML格式返回结果,这些结果将在请求的< code>success部分进行解析。 这是再次出现错误消息:

  • 问题内容: 我看到以下错误: 使用此代码: 是什么原因引起的,如何解决? 问题答案: 在当前域之外发出ajax请求时,Javascript是受限制的。 例1:您的域名为example.com,并且您想向test.com提出请求=>您不能。 例2:您的域名是example.com,并且您想向inner.example.com发送请求,但是您不能。 例3:您的域名为example.com:80,并且您

  • 我的JSON字符串是: 我想要实现的是,当JSON中没有提供schemaVersion时,能够在默认情况下反序列化到SubClassV1,但即使在Superclass中将schemaVersion初始化为“1.0”时,我仍然会收到以下错误:

  • Unix was not designed to stop you from doing stupid things, because that would also stop you from doing clever things. — Doug Gwyn 你曾经遇到过误删除了某些文件而又希望恢复的情形吧? 本书中提及的最重要的技巧就是将 Puppet 的 配置清单(manifests)纳入像