我很难理解字节码操作/增强与Java Instrumentation API之间的依赖关系。
根据我对任何字节码操作/增强的理解,我们有两种选择
*.class
,然后编译为其他一些类应该执行库/应用程序来进行操作。
使用Java Instrumentation API,这意味着
必须提供javaagent。
我不确定的事情:
请注意,我对这个主题的经验非常少,所以我有可能误解或错过了一些概念。我试图将这个复杂的主题归纳为一些简单的解释,即使它是非常笼统的或使用类推论证的。
最佳答案
可以将已编译的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代理来应用类文件检测,该代理提供对
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的独特功能。但是,其他库(例如ASM或Javassist)与Instrumentation
API没有关系,并且需要您实现自己的类文件转换器,在其中使用这些库的API来处理所提供的类文件。