2010-11-09 66 views
11

内:Is there a way to obtain the bytecode for a class at runtime?的Java:在运行时获得类字节码从与同一JVM

我加入耐久性的Clojure,我终于在这里,我准备添加的功能点。在Clojure中,函数使用invoke方法(等等)将字节编译为类。这样,功能就是一流的。为了使这些持久化,我需要序列化和反序列化这些类。如何在不访问.class文件的情况下获取该类的字节码?

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

仅使用Serializable来设置和获取Class对象是不够的。在反序列化之后,我需要加载这个类,并且在后续的VM实例中,可能会出现类名冲突。我需要修改字节码来将类重命名为反序列化/类加载时唯一的东西。

+0

我绝不是这个话题的专家,但它可能是值得尝试坚持函数*定义*而不是底层字节码。你可以重新将函数重新编译为字节码。 – mikera 2010-11-09 20:27:32

回答

4

你可以编写自己的ClassLoader,并在加载类时记录下字节码的方案。

您需要覆盖findClass才能自己查找类文件,将其加载到内存中,将数据保存到某处(以便稍后序列化),然后调用defineClass在JVM中定义该类。

+0

p.s.我认为代理与主程序运行在同一个虚拟机上,所以它们可能会为你工作。 – 2010-11-09 06:46:30

+0

Javassist已经实现了这个方案,并提供了一个方便的访问有问题的字节码。 BCEL也可能做类似的事情。 – Blaisorblade 2011-07-08 15:02:53

+0

最小的实现是相当微不足道的:https://gist.github.com/Felk/ed4375d27c755e21d0e6893847286d93(我知道这个职位是7岁) – Felk 2017-09-15 13:56:45

3

除非你是通过一个棘手的类加载器中运行的代码,你应该能够做这样的事:如果

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”的文件中的内容手柄...它可以被发现。

注意事项。有些类加载器可能:

  • 没有让所有打开的“.class”资源,
  • 给你一个加密的字节码流,或
  • 给你的是字节码,也没有什么是正在运行中,由于到由类加载器执行的一些即时转换。

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

+1

唉,这只有在.class首先作为资源写入磁盘时才有效。 Clojure不会这样做。 – alyssackwan 2010-11-10 23:04:04

+0

这个类在另一个类中定义时也有问题。例如:'class Foo {class Bar {}}'应该变成“/Foo$Bar.class” – iliis 2013-04-21 17:01:31

+1

'clazz.getResourceAsStream('/'+ clazz.getName()。replace('。','/')+ “.class”)'适用于内部类,甚至具有'null' ClassLoader的引导类。 – Holger 2013-11-07 14:51:08

1

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