用 Java 代理修改已经加载的 class?

Modify already loaded class with Java agent?

目前我正在尝试修改驻留在已由 JVM 加载的 classes 中的方法体。我知道 JVM 实际上不允许更改已经加载的 classes 的定义。但是我的研究让我想到了像 JRebel 或 Java Instrumentation API 这样的实现,它们都使用基于代理的方法。我知道如何在代表 Javassist 加载 class 之前立即执行此操作。但考虑到例如在应用程序启动时加载 class 定义的 EJB 环境中的 JRebel,在 JVM 加载的 classes 上不应该修改字节码吗?

我不知道您可以使用工具 API 重新定义 classes(请参阅@Holger 的回答)。然而,正如他指出的那样,这种方法存在一些重大局限性。此外,javadoc 说:

"This method is intended for use in instrumentation, as described in the class specification."

使用它来实质性地改变 class 的语义是......从 Java 类型系统的角度来看,各种各样的坏事。

好了,你学会了把Instrumentation API exists and it offers redefinition of classes当作一个操作。因此,是时候重新考虑“JVM 实际上不允许更改已经加载的 classes 的定义”的初始前提了。

你应该注意到

  • 如链接所示,Instrumentation API 是标准 API
  • 的一部分
  • 然而,对 classes 重新定义的支持是可选的。你可以ask当前JVM是否支持这个特性
  • 可能限于不支持每个class;你可能会问 whether it’s possible for a particular class
  • 即使支持,变化也可能有限,引用the documentation:

    The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. These restrictions maybe be lifted in future versions.

  • at the time you perform the redefinition, there might be threads executing code of methods of these classes; these executions will complete using the old code then


因此 Instrumentation 仅用于调试和分析等。

但是其他框架,如 EJB 容器,在生产代码中提供 class 重新加载,通常求助于创建新的 ClassLoaders,这些 ClassLoaders 创建不同版本的 classes,然后是完全独立于旧版本。

在Java运行时环境中,class的身份由一对<ClassLoader, Qualified Name>组成,而不仅仅是一个合格的名字…