在运行时替换某些方法的内容

我想在运行时替换一些方法的内容。

我知道我可以使用javassist ,但它不起作用,因为我想要增强的类已经被系统classLoader加载。

我该怎么做,在运行时替换方法的内容? 我应该尝试卸载课程吗? 我怎样才能做到这一点 ? 我看到它是可能的,但我无法弄清楚如何做到这一点。

如果可能的话,我想避免使用外部库,我想自己编写代码。

更多信息: – 我想要增强的类包含在一个框架中(在一个jar文件中) – 我的代码实际上是这个框架的一个插件 – 我的插件运行的框架有自己的classLoader ,但是这个classLoader没有加载自己的类(它将它们委托给系统类加载器) – 我正在使用的框架是Play

谢谢您的帮助 !

可以使用Javaassist以及其他任何字节码工程库来完成。 神奇之处在于Java Attach API ,它允许程序附加到正在运行的JVM(并修改加载的类)。

它可以在com.sun.tools.attach包中找到,顾名思义,它特定于Oracle JVM。 尽管如此,像jstackjmap这样的JDK工具使用它来支持它们“附加到运行JVM”function,因此可以肯定地说它就在这里。

Attach API上的文档具有相当的描述性,此Oracle博客文章演示了如何在运行时附加代理。 一般来说,它归结为:

  • 使用premain等,使转换程序成为“常规” -javaagent方式
  • premain重命名为agentmain
  • 创建一个临时JAR文件,其中包含您的代理类,并且有一个清单,将Agent-Class指向您的代理( agentmain )类,并将Can-Retransform-Classes设置为true
  • 获取目标JVM的PID(可能是相同的进程),并将临时jar附加到它

值得庆幸的是,API可以在您没有太多工作的情况下执行此操作,但如果您在运行时执行JAR生成,则打包代理所需的所有类可能有点棘手。

我希望包含一个演示代理,演示在运行时附加一个分析器,但它最终过于冗长而无法发布。 尽管如此,我还是把它放在了Github的回购中 。

这种方法的一个警告是,它使您的程序依赖于tools.jar附带的tools.jar ,而JRE中不存在这些工具.jar。 您可以通过将tools.jar与应用程序一起发送(或解压缩)来解决此问题,但您仍需要在应用程序中提供Attach API所需的attach本机库。 我已经包含了我在上面链接的存储库中找到的所有平台的库,尽管你也可以自己获取它们。

根据您的使用情况,这可能是理想的,也可能不是理想的。 但它确实有效!


这个问题并不清楚,但是如果您想要做的是在运行时使用您自己的类完全“热交换”类,则不需要使用任何字节码操作库。 相反,您可以单独编译您的类( 确保相同的包,类名等 ),并在目标类上调用transform时简单地返回新类的字节。

正常的ClassLoaders在定义后不支持取消定义或修改类。 因此,插件无法修改框架的行为,除非该框架为此类自定义提供了挂钩。

您可以创建一个自定义类加载器,它从其父类加载器中隐藏一些类,而是重新定义它们,添加您可能需要的任何检测。 但框架在插件之前加载,并将使用自己的类加载器解析类。 因此它将继续使用类的未经检测的版本。

避免这种情况(我能想到的)的唯一合理方法是首先出现:如果你的代码首先被启动,它可以引入一个用于加载框架的类加载器。 但这意味着您必须有一些方法将代码作为框架的包装器进入链中。 不确定这是否适用于您的情况。

更新回复评论:
为了创建一个类Loader来构造一些类,你必须覆盖它的loadClass方法。 如果您的许可允许使用GPL代码,您可以在默认实现中查看OpenJDK如何执行此操作 。 您只需要为您不想隐藏的类提供父类加载器。

隐藏父版本后,您仍然需要修改该类。 也许BCEL类加载器可以帮助你。 或者从包含修改版本的jar文件加载类。 或类似的东西。