我很难理解Bytecode操纵/增强和Java Instrumentation API之间的依赖关系。
根据我对字节码操作/增强的理解,我们有两种选择
*.class
,然后再编译为其他一些
库/应用程序应执行操作。我不确定的事情:
是否存在诸如构建时字节码操作之类的东西,有哪些框架/库支持(例如Javassist,ASM)它们使用某种通用方法还是只是读取并解析字节码,然后为您提供修改方式?
加载时间操纵是否仅依赖Java Instrumentation API?意味着所有可用的框架/库(例如Javassist,ASM)都使用javaagent进行操作吗?
请注意,我对这个主题的经验非常少,所以我有可能会误解或错过一些概念。我试图将这个复杂的主题归纳为一些简单的解释,即使它是非常笼统的或使用类推论证的。
答案 0 :(得分:0)
将已编译的Java类文件视为byte[]
数组,其中包含特定Java类的任何信息。在本文中,检测是指将字节数组后处理为不同形状的过程,而与该过程何时发生或如何发生无关。可以在编译和类加载之间的任何时间应用检测。在Java中,类甚至可以be instrumented after it has been loaded,但不改变其形状,即添加/删除字段或方法。但是无论何时应用工具,概念都保持不变,即重新排列代表已编译Java类的字节数组。
我知道的任何字节码操作库都可以处理来自任何来源的类文件。通常,这些库中最通用的输入是一个简单的字节数组,为方便起见,可以从类加载器中加载该字节数组。可以通过ClassLoader.getResourceAsStream
方法从类加载器中查找类文件,并将类文件的名称作为参数。例如:
classLoader.getResourceAsStream("some/Sample.class")
应为虚构的some.Sample
类解析类文件。通常,这是因为类加载器需要定位类文件(字节数组)以便在首次请求类时加载该类。
在构建期间,类文件通常位于特定的文件夹中,例如在Maven版本的 target / classes 文件夹中。要检测这些类,只需要找到那些文件,将它们读入字节数组,然后写回更改的结果。例如,您可以通过编写自己的Maven插件来执行此操作,例如,在其中可以使用ASM调整文件。为了方便起见,您还可以使用更高级的库,例如Byte Buddy's Maven plugin into which you can load your own plugin,并完全避免使用Maven插件API甚至字节码API。 (有关信息,我是Byte Buddy的作者。)
在运行时,您可以做类似的事情,即找到位于某个文件夹或 jar 文件中的类文件,找到这些类并对其进行调整,然后再由应用程序加载它们。但是,由于jar文件也可能会被也会受到影响的其他应用程序使用,因此这并不总是可以正常工作。另外,这将要求您的用户从其应用程序中显式激活此工具。因此,通常使用Java代理来应用类文件检测,该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;
});
此更改随后隔离到应用程序,并且不会更改原始的类文件。工具API并不意味着使用任何库来修改类文件,这完全取决于您,每个人都在使用某种类型的库,甚至直接使用字节数组。诸如Byte Buddy之类的高级库甚至不需要您实现自己的类文件转换器,而是通过AgentBuilder
API拥有自己的抽象,但是该API确实在幕后创建了一个类文件转换器以利用工具API的独特功能。但是,其他库(例如ASM或Javassist)与Instrumentation
API没有关系,因此需要您实现自己的类文件转换器,在其中使用这些库的API来处理呈现的类文件。