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

Java 在运行时替换某些方法的内容,java,playframework,runtime,classloader,javassist,Java,Playframework,Runtime,Classloader,Javassist,我想在运行时替换一些方法的内容 我知道我可以使用javassist实现这一点,但它不起作用,因为我想要增强的类已经由系统类加载器加载 如何在运行时替换方法的内容?我应该试着把课堂上的东西卸下来吗?我该怎么做?我认为这是可能的,但我不知道怎么做 如果可能的话,我希望避免为此使用外部库,我希望自己编写代码 更多信息: -我想要增强的类包含在一个框架中(在一个jar文件中) -我的代码实际上是这个框架的一个插件 -我的插件运行的框架有自己的类加载器,但这个类加载器不加载自己的类(它将它们委托给系统类加

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

我知道我可以使用javassist实现这一点,但它不起作用,因为我想要增强的类已经由系统类加载器加载

如何在运行时替换方法的内容?我应该试着把课堂上的东西卸下来吗?我该怎么做?我认为这是可能的,但我不知道怎么做

如果可能的话,我希望避免为此使用外部库,我希望自己编写代码

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


谢谢你的帮助

普通类加载器不支持在定义类之后取消定义或修改类。因此,插件不能修改框架的行为,除非框架为此类定制提供挂钩

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

避免这种情况的唯一合理方法(我可以想到)是先到那里:如果代码首先启动,它可以引入一个类加载器来加载框架。但这意味着您必须有某种方法将代码作为框架的包装器放入链中。不确定这在你的情况下是否可行

更新回复评论:
为了创建一个IDE某些类的类加载器,您必须重写它的方法。如果您的许可允许使用GPL代码,您可以查看默认实现中的。对于那些不想隐藏的类,您只需遵从父类装入器

隐藏父版本后,仍然必须修改该类。也许警察可以帮你。或者从包含修改版本的jar文件加载该类。或者类似的东西。

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

它可以在包中找到,顾名思义,它特定于Oracle JVM。尽管如此,JDK工具,如
jstack
jmap
使用它来支持它们的“连接到正在运行的JVM”特性,因此可以肯定地说它已经存在了

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

  • 使用
    premain
    等,以“常规的”
    -javaagent
    方式制作一个重传程序
  • premain
    重命名为
    agentmain
  • 创建一个包含代理类的临时JAR文件,并具有指向代理(
    agentmain
    -containing)类的清单,并且
    可以重新转换类
    设置为
    true
  • 获取目标JVM的PID(可能是同一进程),并将临时jar附加到它
谢天谢地,API可以做到这一点,而您无需做太多工作,不过如果您在运行时生成JAR,那么打包代理所需的所有类可能有点棘手

我本来希望包含一个演示代理,演示如何在运行时附加一个分析器,但结果太长,无法发布。尽管如此,我还是把它放在了一个盒子里

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

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



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

Play!它是开源的。您可以直接在源代码中更改该方法,并构建自己的jar以供使用。您可以为要更改的类编写子类吗?@Jeffrey我可以,但我想为社区编写一个“可用”插件,因此我希望避免修改源代码code@Code-大师不,我不能,这是一个静态类。因此,它的方法是通过classI直接调用的,而不是“先到那里”。但是你说了一些关于“隐藏”某些类的事情。你的意思是我可以创建另一个类加载器,让它继承框架的类加载器,当需要我想要增强的类时,我自己加载它?(如果要求另一个类,我让超级类加载它?)。这样行吗?如果是,如何加载该类?在其他地方拥有该类的副本并加载该副本?查找类、增强类并返回新创建的类?我试图在play framework定义的类加载器中添加日志,但当