是否可以在没有外部类的情况下序列化匿名类? [英] Is it possible to serialize anonymous class without outer class?

查看:108
本文介绍了是否可以在没有外部类的情况下序列化匿名类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在网上进行了一项小型研究并审查了该网站上的相关主题,但答案是矛盾的:有些人说这是不可能的,有些人说这是可能的,但很危险。

I made a small research on web and reviewed related topics on this site, but the answers were contradictory: some people said it is not possible, others said it is possible, but dangerous.

目标是将匿名类的对象作为RMI方法的参数传递。由于RMI要求,此类必须是可序列化的。这没有问题,很容易使类Serializable。

The goal is to pass an object of the anonymous class as a parameter of the RMI method. Due to RMI requirements, this class must be serializable. Here's no problem, it is easy to make class Serializable.

但我们知道内部类的实例持有对外部类的引用(而匿名类是内部类) 。因此,当我们序列化内部类的实例时,外部类的实例被序列化以及字段。这里是问题出现的地方:外部类不可序列化,更重要的是 - 我不想序列化它。我想要做的只是发送匿名类的实例。

But we know that instances of inner classes hold a reference to an outer class (and anonymous classes are inner classes). Because of this, when we serialize instance of inner class, instance of outer class is serialized as well as a field. Here's the place where problems come: outer class is not serializable, and what's more important - I do not want to serialize it. What I want to do is just to send instance of the anonymous class.

简单示例 - 这是一个RMI服务,其方法接受Runnable:

Easy example - this is an RMI service with a method that accepts Runnable:

public interface RPCService {    
    Object call(SerializableRunnable runnable);
}

以下是我要调用方法的方法

And here is how I'd like to call the method

void call() {
     myRpcService.call(new SerializableRunnable() {             
         @Override
         public Object run {
             System.out.println("It worked!");
         }
     }        
}

正如您所看到的,我想要做的是向另一方发送操作 - 系统A描述应该在系统B上运行的代码。就像用Java发送脚本一样。

As you can see, what I want to do is to send an "action" to the other side - system A describes the code, that should be run on system B. It is like sending a script in Java.

如果可能的话,我很容易看到一些危险的后果:例如,如果我们访问一个字段或捕获外部类的最终变量来自Runnable - 我们会遇到麻烦,因为调用者实例不存在。另一方面,如果我在Runnable中使用安全代码(编译器可以检查它),那么我没有看到禁止此操作的理由。

I can easily see some dangerous consequences, if this was possible: for example if we access a field or captured final variable of outer class from Runnable - we'll get into a trouble, because caller instance is not present. On the other hand, if I use safe code in my Runnable (compiler can check it), then I don't see reasons to forbid this action.

所以如果有人知道,怎么 writeObject() readObject()方法应该在匿名类中正确覆盖或者如何引用外部类 transient 或者解释为什么在java中它是不可能的,它会非常有帮助。

So if someone knows, how writeObject() and readObject() methods should be properly overriden in anonymous class OR how to make reference to outer class transient OR explain why it is impossible in java, it will be very helpful.

UPD
还有一件重要的事情需要考虑:外部class不会出现在执行方法的环境中(系统B),这就是为什么应该完全排除有关它的信息以避免 NoClassDefFoundError

推荐答案

您可以尝试制作 Caller.call() a static 方法。

You could try making Caller.call() a static method.

但是,匿名类仍然需要在反序列化序列化实例的上下文中可用。这是不可避免的。

However, the anonymous class would still need to be available in the context in which you deserialize the serialized instance. That is unavoidable.

(很难想象匿名类可用但封闭类不可用的情况。)

(It is hard to imagine a situation where the anonymous class would be available but the enclosing class isn't.)


因此,如果有人可以展示,我如何在我的匿名类中正确覆盖writeObject和readObject方法...

So, if someone can show, how I can properly override writeObject and readObject methods in my anonymous class ...

如果你使 Caller.call()静态,那么你会这样做就像你想的那样,如果它是一个命名的类,我想。 (我相信你可以找到适合自己的例子。)

If you make Caller.call() static, then you would do this just like you would if it was a named class, I think. (I'm sure you can find examples of that for yourself.)

确实,(模拟匿名类可用性问题) ) 有用。这里, static main 方法替换了静态Classer.call()方法。程序编译并运行,表明在静态方法中声明的匿名类可以被序列化和反序列化。

Indeed, (modulo the anonymous class availability issue) it works. Here, the static main method substitutes for a static Classer.call() method. The program compiles and runs, showing that an anonymous class declared in a static method can be serialized and deserialized.

import java.io.*;

public class Bar {

    private interface Foo extends Runnable, Serializable {}

    public static void main (String[] args) 
            throws InterruptedException, IOException, ClassNotFoundException {

        Runnable foo = new Foo() {
            @Override
            public void run() {
                System.out.println("Lala");
            }
        };

        Thread t = new Thread(foo);
        t.start();
        t.join();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(foo);
        oos.close();
        Foo foofoo = (Foo) new ObjectInputStream(
            new ByteArrayInputStream(baos.toByteArray())).readObject();

        t = new Thread(foofoo);
        t.start();
        t.join();
    }
}








另一个要记住的重要事项是:调用者类在执行该方法的环境中不存在,所以我想排除所有序列化期间有关它的信息,以避免 NoClassDefFoundError

Another important thing to remember about: the Caller class is not present in the environment, that executes the method, so I'd like to exclude all information about it during serialization to avoid NoClassDefFoundError.

没有办法避免这样做。远程JVM中的反序列化抱怨的原因是类描述符包含对外部类的引用。反序列化方需要解析该引用,即使您设法破坏引用,即使您从未在反序列化对象中显式或隐式使用合成变量。

There is no way to avoid that. The reason that deserialization in the remote JVM is complaining is that the class descriptor includes a reference to the outer class. The deserializing side needs to resolve that reference even if you managed to clobber the reference, and even if you never explicitly or implicitly used the synthetic variable in the deserialized object.

问题是远程JVM的类加载器在加载内部类的类文件时需要知道外部类的类型。它需要进行验证。反思需要它。垃圾收集器需要它。

The problem is that the remote JVM's classloader needs to know the type of the outer class when it loads the classfile for the inner class. It is needed for verification. It is needed for reflection. It is needed by the garbage collector.

没有解决方法。

(我不确定是否这也适用于静态内部类...但我怀疑它确实如此。)

(I'm not sure if this also applies to a static inner class ... but I suspect that it does.)


尝试序列化没有外部类的匿名Runnable实例不仅指序列化问题,还指在另一个环境中执行任意代码的可能性。很高兴看到一个JLS引用,描述了这个问题。

Attempting to serialize anonymous Runnable instance without outer class refers not only to a serialization problem, but to a possibility of arbitrary code execution in another environment. It would be nice to see a JLS reference, describing this question.

没有JLS参考。 JLS中未指定序列化和类加载器。 (类初始化是......但这是一个不同的问题。)

There is no JLS reference for this. Serialization and classloaders are not specified in the JLS. (Class initialization is ... but that is a different issue.)

可以通过RMI在远程系统上运行任意代码。但是,您需要实现RMI动态类加载才能实现此目的。以下是参考:

It is possible to run arbitrary code on a remote system via RMI. However you need to implement RMI dynamic class loading to achieve this. Here is a reference:

  • http://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/rmi/spec/rmi-arch.doc.html#280

请注意,为RMI添加远程类的动态类加载重大安全问题。你必须考虑类加载器泄漏等问题。

Note that adding dynamic class loading for remote classes to RMI introduces significant security issues. And you have to consider issues like classloader leaks.

这篇关于是否可以在没有外部类的情况下序列化匿名类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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