Java:在运行时从同一JVM中获取类的字节码

Java:在运行时从同一JVM中获取类的字节码,java,clojure,persistence,bytecode,java-bytecode-asm,Java,Clojure,Persistence,Bytecode,Java Bytecode Asm,有关: 我正在为Clojure添加耐久性,我终于准备好添加函数了。在Clojure中,函数通过invoke方法(以及其他方法)被字节编译成类。这样,函数是第一类的。为了使这些持久,我需要序列化和反序列化这些类。如何在不访问.class文件的情况下获取类的字节码 如果我错了,请纠正我,但是使用代理需要生成一个单独的VM,并且代理连接到第一个VM。我需要在同一个虚拟机上完成 仅使用Serializable来设置和获取类对象是不够的。在反序列化时,我需要加载该类,在后续的VM实例中,可能会发生类名冲突

有关:

我正在为Clojure添加耐久性,我终于准备好添加函数了。在Clojure中,函数通过invoke方法(以及其他方法)被字节编译成类。这样,函数是第一类的。为了使这些持久,我需要序列化和反序列化这些类。如何在不访问.class文件的情况下获取类的字节码

如果我错了,请纠正我,但是使用代理需要生成一个单独的VM,并且代理连接到第一个VM。我需要在同一个虚拟机上完成


仅使用Serializable来设置和获取类对象是不够的。在反序列化时,我需要加载该类,在后续的VM实例中,可能会发生类名冲突。我需要修改字节码,以便在反序列化/类加载时将类重命名为唯一的类。

除非您通过复杂的类加载器运行代码,否则您应该能够执行以下操作:

Class<?> clazz = ....
String className = clazz.getCanonicalName();  // e.g. "foo.Bar"
String resourceName = ... // map className to a resource name; e.g. "/foo/Bar.class" 
InputStream is = clazz.getClassLoader.getResourceAsStream(resourceName);
Class clazz=。。。。
字符串className=clazz.getCanonicalName();//e、 g.“富吧”
字符串resourceName=…//将类名称映射到资源名称;e、 g.“/foo/Bar.class”
InputStream=clazz.getClassLoader.getResourceAsStream(resourceName);
这将为您提供“.class”文件内容的句柄。。。如果能找到的话

警告。某些类加载器可能:

  • 根本不允许打开“.class”资源
  • 给你一个加密的字节码流,或者
  • 由于类加载器执行了一些动态转换,因此提供的字节码与正在运行的字节码并不完全相同

如果这种方法不起作用,您将几乎没有选择余地,因为JVM不提供访问加载的实际字节码的方法。

您可以编写自己的
类加载器
,并创建一个在加载类时记录字节码的方案


您需要重写
findClass
来自己查找类文件,将其加载到内存中,将数据保存在某个位置(用于以后的序列化),然后调用
defineClass
在JVM中定义该类。

您也可以使用Java Instrumentation API来实现此目的。在调用defineClass之前,您可以访问类文件的字节。你也可以改变它们

另外,我认为代理与主程序运行在同一个VM中,因此它们可能适合您。Javassist已经实现了这个方案,并提供了对所讨论字节码的某种方便的访问。BCEL也可能做类似的事情。最小实现非常简单:(我知道这篇文章已经7年了)我绝对不是这方面的专家,但可能值得尝试持久化函数定义,而不是底层字节码。然后,当您重新加载函数时,您可以将其重新编译为字节码。唉,只有当.class首先作为资源写入磁盘时,这才有效。Clojure不这样做。当类在另一个类中定义时,它也有问题。例如:
class Foo{class Bar{}}
应该变成“/Foo$Bar.class”
clazz.getResourceAsStream('/'+clazz.getName().replace('.','/')+“.class”)
适用于内部类,甚至适用于具有
null
ClassLoader的引导类。