【问题标题】:Modify already loaded class with Java agent?使用 Java 代理修改已加载的类?
【发布时间】:2015-07-05 15:20:35
【问题描述】:

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

【问题讨论】:

  • 您希望类的加载后修改如何与 JVM 的编译交互?
  • 你可以阅读这个:java.dzone.com/articles/reloading-java-classes-401 这很有趣。
  • 根据您的需要,AspectJ(允许您向现有对象添加代码)或 OSGi(基本上是类加载器)可能值得一看。

标签: java bytecode javaagents jrebel


【解决方案1】:

好吧,您了解到Instrumentation API 存在并且它提供redefinition of classes 作为操作。因此,是时候重新考虑“JVM 实际上不允许更改已加载的类的定义”的初始前提了。

你应该注意

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

    重新定义可能会改变方法体、常量池和属性。重新定义不得添加、删除或重命名字段或方法,更改方法的签名或更改继承。这些限制可能会在未来的版本中取消。

  • 在执行重定义时,可能有线程在执行这些类的方法代码;然后这些执行将使用旧代码完成

所以 Instrumentation 仅对调试和分析等有用。

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

在 Java 运行时环境中,类的标识由一对<ClassLoader, Qualified Name> 组成,而不仅仅是一个限定名……

【讨论】:

  • 谢谢霍尔格。所以我现在知道的是,通过更改方法体(完全满足我的需要)重新定义一个类仍然是可能的。所以文档说,当 Java 代理启动时,可以获取 Instrumentation 对象作为参数。当我想重新定义任意类时,是否允许存储对此对象的引用并稍后从我正在运行的应用程序调用该对象的方法?
  • 它可以工作,并且对这种使用模式没有额外的限制,您只需要注意您使用的功能可能并非每个 JVM 都支持。 this answer 的末尾是一个应用程序示例代码,该应用程序(重新)作为 Java 代理启动自身,同时已经运行以获取 Instrumentation API。
【解决方案2】:

我不知道您可以使用检测 API 来重新定义类(请参阅 @Holger 的回答)。然而,正如他所指出的,这种方法存在一些重大限制。此外,javadoc 说:

“此方法旨在用于instrumentation,如类规范中所述。”

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

【讨论】:

  • 这就是我所期待的,也是我害怕的。谢谢斯蒂芬!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-05
  • 2013-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多