当前位置: 首页 > 知识库问答 >
问题:

使Java类从任何类加载器可见

李浩邈
2023-03-14

我使用Java代理(Agent.class)来转换程序(Program.class)中的方法,其中包括对代理类的调用。

public Program.getMultiplier()F:
    ALOAD 1
    ALOAD 2
    FDIV
+   INVOKESTATIC Agent.getCustomMultiplier(F)F
    FRETURN

我已经检查了Agent和Program类的类加载器及其父类,它们的层次结构如下:

  • 特工。类:AppClassLoader

当程序执行添加的INVOKESTATIC指令时,它会抛出一个ClassNotFoundExcture它找不到Agent类,因为它是由不同的类加载器加载的。

作为一个临时解决方案,我尝试强制AppClassLoader成为带有反射的URLClassLoader的父级,反射在较旧的Java版本中工作,但自Java 12以来已被删除。

是否有更可靠的方法确保我的代理类在任何类装入器中都可见?

共有2个答案

龚睿
2023-03-14

您可能能够执行一些特定的操作,这些操作适用于URLClassLoader,但并非所有类都由URLClassLoader的实例加载。任何OSGi项目都不会,大多数web服务器也使用自己的类加载器来支持热重新加载等。

据我所知,没有办法随便更新一些“所有类加载器的全局父类”或注入一个;没有这样的父级,即使有,类加载器也可以完全忽略它的父级。

因此,一般的答案是:不,你不能那样做。

但是,让我们戴上黑客帽吧!

你已经是特工了。作为代理,您要做的事情之一是在类被加载时“见证”它们。只需调用. addTransator在您的agentmain中获得的仪表实例并注册一个。

当您注意到正在加载程序类时,请执行以下操作:

  • 获取字节码并通过ASM,BCEL,Bytecode Buddy或任何其他java'类文件读取器/变压器'框架将其抛出。
  • 也从你的代理代码中打开一个类(我不会使用代理代码本身,我会创建一个名为编程方法或诸如此类的类作为容器——里面的一切都是为程序使用/为您的将代理人“注入”到那个程序中。
  • 将程序中的每个静态成员直接添加到程序中。在此过程中,修改所有访问(包括INVOKESTATIC和读/写字段操作码)上的类型名,其中etypename为编程方法,并使其成为目标类的完全限定名称。
  • 注入INVOKESTATIC,就像你已经做的那样,但是,重写它,让它进入自己的类,就像你刚刚复制了那里的所有静态方法和字段一样。
  • 然后从变压器返回该修改类的字节码。

这100%保证您不可能遇到任何模块或类路径边界问题,并且它将适用于任何类加载器抽象,保证,但有一些警告:

  • 只是不要试图和任何事情扯上关系。使其成为所有静态方法和字段。如果必须,您可以使用IdentityHashMap创建假实例字段(例如静态IdentityHashMap

这都是非常复杂的事情,但您似乎已经解决了如何注入INVOKESTATIC调用,所以,我认为您知道如何做到这一点。

这正是lombok在eclipse中“修补”某些方法所做的,以确保保存操作、自动格式化、,语法突出显示不中断-lombok在适当的地方注入生成注释的知识,并以这种精确的方式进行,因为eclipse使用了一个名为Equinox的类加载器平台,这使得任何其他解决方案都有问题。你可以从中寻找灵感或指导方针,尽管它并没有很好的文档记录。您特别关注:

  • lombok。日食代理eclipseent中的包sourceroot

请注意,您可能还会对下一个方法感兴趣:lombok.patcher的“插入”不会移动方法——它直接在那里注入方法的主体(“内联”)。这需要对堆栈进行一些严重的欺骗,并且只建议用于极其简单的一行式方法,并且对于这个问题可能是过度和不必要的火力。

免责声明:大部分是我写的。

纪俊良
2023-03-14

让代理监听新ClassLoaders的创建,然后将它们的实例附加到新ClassLoaders。

通过这种方式,您可以保留“代理侦听类加载器”接口,即使它现在扩展到您希望代理侦听的一个平台类加载器之外。

 类似资料:
  • 问题内容: 我是Java UI编程的新手。 如何在Java中从主类调用UI类 基本上 运行程序时如何加载GUI。目前,当我运行代码时什么也没有发生。 GUI CLass 根据评论中的要求,我已经上传了GUI类代码 问题答案:

  • 因此,我有一个类加载器加载一个类,如下所示: 类在运行时位于另一个jar中。以及是在主jar中生成的,然后我取消了它的归档,所以依赖项就在那里。 如何从外部访问其他依赖项。我加载的类文件? 例外情况:

  • 如果我有一个内部类声明,例如: 其次是: A$B内部类也会加载吗?如果B内部类没有被声明为“静态”呢?

  • 问题内容: 有谁知道如何以编程方式找出java类加载器实际从何处加载类? 我经常在大型项目中使用类路径,并且手动搜索并不是一个好的选择。我最近遇到了一个问题,其中类加载器正在加载不正确的类版本,因为它位于两个不同位置的类路径上。 那么,如何让类加载器告诉我实际的类文件来自磁盘上的什么地方呢? 编辑:如果类加载器由于版本不匹配(或其他原因)而实际上无法加载该类,该怎么办,在读取它之前我们是否能找出它

  • 问题内容: 我正在使用一些第三方代码,这些代码在给定“ -classpath”命令行参数时不会设置java.class.path,而是仅创建一个类加载器,将命令行中指定的类路径中所有项的所有url添加到类加载器,然后将其设置为上下文类加载器。在我编写的此代码的插件类中,我获得了该类加载器的实例,并且需要以某种方式使用它来获取基础类路径,以便可以在JavaCompiler.getTask(… ),并

  • 我一直在Java玩简单的自定义类加载器,到目前为止,对于非模块相关的类来说,一切都按照预期工作。但是,我似乎找不到任何方法来使用我的类加载器从模块加载类,即使与模块相关的方法已经被重载。我尝试做的是从模块“HelloModularWorld”加载一个类,并运行它的main。然而,当我指定包所在的目录时,它将“正常”加载,并报告为在未命名模块中。我错过了什么? 类加载器只是从文件系统的其他地方加载类