字节码操纵/增强和Java Instrumentation API

3

我很难理解字节码操作/增强和Java Instrumentation API之间的依赖关系。

根据我的理解,要进行任何字节码操作/增强,我们有两个选项:

  • 编译时 - Java类被编译为*.class,然后需要执行其他库/应用程序来进行操作。
  • 加载时 - 仅通过使用Java Instrumentation API,这意味着必须提供特定的javaagent。

我不确定的事情:

  • 是否存在编译时字节码操作,有哪些支持该操作的框架/库(例如Javassist、ASM)?它们是否使用共同的方法或只是读取和解析字节码,然后提供一种修改它的方式?

  • 加载时操作是否只依赖于Java Instrumentation API?这意味着所有可用的框架/库(例如Javassist、ASM)都使用javaagent来进行操作吗?

请注意,我对这个主题的经验非常少,所以有可能我误解或错过了一些概念。我正在试图将这个复杂的主题简化为一些简单的解释,即使它将非常一般或使用比喻进行演示。

1个回答

4
将一个已编译的Java类文件视为包含特定Java类的任何信息的byte[]数组。在此上下文中,仪器化是指将这个字节数组后处理成不同的形状,无论何时或如何进行处理都是独立的。仪器化可以应用于从编译到类加载之间的任何时间;在Java中,甚至可以在类被加载后 进行仪器化 ,但不能改变其形状,例如添加/删除字段或方法。但不管仪器化何时应用,概念都保持不变,即重新排列表示已编译Java类的字节数组。
我所知道的任何字节码操作库都允许处理来自任何来源的类文件。通常,这些库的最通用输入是一个简单的字节数组,可以选择从类加载器中加载以方便操作。可以通过使用类文件名作为参数来通过类加载器查找类文件。例如:
classLoader.getResourceAsStream("some/Sample.class")

应该为一个虚构的some.Sample类解决类文件。通常,这是因为在第一次请求加载类时,需要通过类加载器定位类文件(字节数组)。

在构建时,类文件通常位于特定文件夹中,例如Maven构建的target/classes文件夹中。要对这些类进行仪器化,您只需要找到这些文件,将它们读入字节数组,然后写回更改后的结果。例如,您可以编写自己的Maven插件,在其中使用ASM调整文件。但是,为了方便起见,您也可以使用更高级的库,例如Byte Buddy的Maven插件,您可以将自己的插件加载到其中,避免使用Maven插件API甚至是字节码API。(我是Byte Buddy的作者。)

在运行时,您可以做非常相似的事情,即找到位于某个文件夹或jar文件中的类文件,找到这些类并在应用程序加载之前进行调整。但是,这种方法不总是有效,因为jar文件可能还被其他应用程序使用,这些应用程序也会受到影响。此外,它还需要用户明确从他们的应用程序中激活此仪器化。因此,通常使用Java代理来应用类文件仪器化,这样可以访问Instrumentation API,从而使其更加方便。该API允许在Java的内部类加载机制中安装钩子,从而可以在加载类的字节数组之前对其进行调整:

instrumentation.addClassFileTransformer(
  (Module module, ClassLoader loader, String name, 
   Class<?> classIfLoaded, ProtectionDomain pd, byte[] classFile) -> {
     byte[] transformed = doSomethingWith(classFile);
     return transformed;
});

这种更改会被隔离到应用程序中,不会更改原始的类文件。Instrumentation API并不意味着使用任何库来修改类文件,这完全由您自己决定,每个人都在使用某种库甚至直接操作字节数组。高级库(例如Byte Buddy)甚至不需要您实现自己的类文件转换器,而是通过AgentBuilder API拥有自己的抽象层,但会在底层创建一个类文件转换器以利用Instrumentation API的独特功能。其他库,如ASM或Javassist,与Instrumentation API没有关系,需要您自己实现类文件转换器,并使用这些库的API处理所提供的类文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接