我在网站上做了一个小研究,并在这个网站上回顾了相关主题,但答案是矛盾的:有人说这是不可能的,有人说这是可能的,但很危险。
目标是传递匿名类的对象作为RMI方法的参数。由于RMI要求,这个类必须是可序列化的。这里没有问题,很容易使类序列化。
但是我们知道内部类的实例包含对外部类的引用(匿名类是内部类)。因此,当我们序列化内部类的实例时,外部类的实例也被序列化为一个字段。问题来了:外部类是不可序列化的,更重要的是——我不想序列化它。我想做的只是发送匿名类的实例。
简单示例-这是一个RMI服务,其方法接受Runnable:
public interface RPCService {
Object call(SerializableRunnable runnable);
}
下面是我想如何调用这个方法
void call() {
myRpcService.call(new SerializableRunnable() {
@Override
public Object run {
System.out.println("It worked!");
}
}
}
如您所见,我想做的是向另一方发送一个“操作”——系统A描述应该在系统B上运行的代码。这就像在Java发送脚本。
如果可能的话,我可以很容易地看到一些危险的后果:例如,如果我们从Runnable访问一个字段或捕获外部类的最终变量,我们会遇到麻烦,因为调用方实例不存在。另一方面,如果我在Runnable中使用安全代码(编译器可以检查),那么我看不到禁止此操作的理由。
因此,如果有人知道如何在匿名类中正确重写writeObject()
和readObject()
方法,或者如何引用外部类transient
或解释为什么在java中不可能,这将非常有帮助。
UPD还需要考虑另一件重要的事情:执行方法的环境(系统B)中不存在外部类,这就是为什么应该完全排除有关它的信息,以避免NoClassDefFoundError
。
上面所述的示例不能在Java中工作,因为匿名内部类是在类Caller中声明的,并且您明确声明类Caller在RPC服务器上不可用(如果我理解正确的话)。请注意,对于JavaRPC,只有数据通过网络发送,类必须已经在客户端和服务器上可用。尊重您的示例是没有意义的,因为看起来您想要发送代码而不是数据。通常情况下,您将在服务器和客户端都可以使用的JAR中拥有可序列化的类,并且每个可序列化的类都应该具有唯一的序列化VersionUID。
如果您足够疯狂,可以使用反射来查找包含对外部类的引用的字段,并将其设置为null
。
您可以尝试使Caller.call()
成为静态
方法。
但是,匿名类仍然需要在反序列化序列化实例的上下文中可用。这是不可避免的。
(很难想象匿名类可用但封闭类不可用的情况。)
所以,如果有人能展示,我如何在我的匿名类中正确重写writeObject和readObject方法。。。
如果您使用调用者。call()
static,然后您可以像处理命名类一样执行此操作。(我相信你自己也能找到这样的例子。)
事实上,(考虑匿名类可用性问题)它是有效的。这里,static main
方法代替了静态类。call()
方法。程序编译并运行,表明在静态方法中声明的匿名类可以序列化和反序列化。
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
。
这是无法避免的。远程JVM中的反序列化抱怨的原因是类描述符包含对外部类的引用。反序列化端需要解析该引用,即使您成功地关闭了该引用,并且即使您从未在反序列化对象中显式或隐式使用过合成变量。
问题是远程JVM的类加载器在为内部类加载类文件时需要知道外部类的类型。这是核查所需要的。这是反思的需要。垃圾收集器需要它。
没有解决办法。
(我不确定这是否也适用于静态
内部类……但我怀疑它确实适用。)
尝试在没有外部类的情况下序列化匿名Runnable实例不仅涉及序列化问题,还涉及在另一个环境中执行任意代码的可能性。这将是很好的看到一个JLS参考,描述这个问题。
对此没有JLS参考。JLS中未指定序列化和类加载器。(类初始化是……但这是另一个问题。)
可以通过RMI在远程系统上运行任意代码。然而,您需要实现RMI动态类加载来实现这一点。以下是参考资料:
请注意,将远程类的动态类加载添加到RMI会带来重大的安全问题。您必须考虑类加载器泄漏等问题。
我的问题是,有什么方法可以让我序列化/反序列化一个名为onlinePlayers的列表,该列表引用了“John1”的实例,它也碰巧在List allPlayers中,而不重复“John1”,同时仍然引用那个对象? 我猜当我反序列化allPlayers时,它将创建不同于原始对象的对象,所以onlinePlayers在反序列化后不可能仍然引用相同的对象。我是否应该编写一个自定义方法,在反序列化后将新创
我已经从源代码处构建并安装了另一个glibc,并且我想让现有的用C++编写的可执行文件与自定义glibc一起运行,以供实验之用。为了做到这一点,我尝试更改可执行文件的加载程序。首先,在/lib64下创建了一个名为的链接,其路径指向新的加载程序 其次,通过文本编辑器修改了可执行文件中的加载器路径,将“/lib64/ld-linux-x86-64.so.2”更改为“/lib64/ld_linux-x8
是否可以在没有实体的情况下使用JpaRepository?在这种情况下,将其替换为DTO。 如下示例所示 这种情况有替代方案吗? 注意:DTO已经映射,但我不想创建视图来将此DTO转换为实体。 我已经验证了这个主题,但没有重大进展,请使用无实体的JpaRepository交互样式 我在试这个 接口- 公共接口BffDTOInterface2{ } 我有这个错误
我想使用并使其直接进入给定的url,而不是从ribbon配置中获取主机。 我知道在Spring,cloud-feign默认与ribbon和eureka一起出现。 根据这个:https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#spring-cloud-ribbon-without-eure
问题内容: 有关一些背景知识和参考,以下是一些Heroku文档页面的一些报价。 从Heroku Node.js支持>激活中: 当应用程序的根目录中有文件时,将使用Heroku Node.js buildpack 。 从Heroku Node.js支持>默认Web进程类型: 首先,Heroku寻找一个Procfile来指定您的进程类型。 如果在构建过程中应用程序的根目录中没有任何内容,则将通过运行[
在我当前的项目中,我使用Envers 5.2.12审核实体更改。由于安全限制,我必须手动创建审核表和reinfo表,它们不能包含外键。如果我在没有外键引用的情况下创建它们,这会影响Envers的正常行为吗?