哪里字节码注入发生的呢?(Where does bytecode injection happen?

2019-08-17 09:02发布

动机

我有一个SomeObject.java文件:

class SomeObject {
   String name;
}

编译它创建了一个包含字节码SomeObject.class文件。

0xCAFEBABE...

如果我们在JVM上使用SomeObject,它是由当前的类加载器加载和一切工作正常。

现在让我们假设,我想有一些动态代码生成。 我可以写我的自定义注释

@Target(ElementType.TYPE)
public @interface Data {
   ...
}

并添加作为改性剂的类声明:

@Data
class SomeObject {
   String name;
}

我还可以保留它与运行时@Retention(RetentionPolicy.RUNTIME)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Data {
   ...
}

哪里是用于字节码注入注释? 是否加载类与适当的运行时类加载器注入字节码保留注释就像这个图所示:

source -(compile)-> bytecode -(classloader bytecode injection)-> injected bytecode -(classloading)-> JVM loaded bytecode   

Answer 1:

是的,这将有可能有你的定制ClassLoader加载类,并通过字节码操作工具,如Javassist进行或ASM进行修改,加载到内存中没有在类文件字节码,而是修改的一个。 虽然有比较容易(和更好的,在我看来)做这件事的方式。

标注处理工具(APT)

由于Java 6,你有APT ,它允许你勾到编译过程(通过-processor中的javac参数)。 随着APT您可以访问代码的AST(抽象语法树),你可以使用编译时直接进行修改javax.lang.model 。 这意味着你的类文件将与必要的修改产生。

在这种情况下,链会像这样的:

source -(compile and performs modifications at model level)-> bytecode already modified - regular class loader -> loads class into memory

后编译处理

可以使用的另一种方法是汇编作为编译后处理之后执行字节码注射。 在这种情况下,你使用的字节码修改工具(再次Javassist进行,ASM等等),它可以执行你需要的时候所需的注释被发现,产生与注入字节码一个新的类文件的修改。

在这种情况下,你的链条是:

source -compile -> bytecode -post-compile-> modified bytecode - regular class loader -> loads class into memory

运行时修改

最后,我们达成运行时字节码的修改。 即使你的想法是可行的,在我看来,我会离开的类加载器的魔法和使用的工具,具有了Javassist也可以让你有动态代理可以修改和重新加载 。

在Javassist进行特定的情况下链将是

source -compile -> bytecode -post-compile-> modified bytecode - regular class loader -> loaded into memory - javassist proxy -> modified class - javassist hot swapper -> re-modified class

代理服务器是不完美的,但(也没有什么)。 你将有一个性能命中,你将不能修改类的公共接口(旁注:APT和后期编译过程可以让你修改类公共接口)。 我可以去多一点就这一点,但我觉得这已经是足够的信息,让您回味无穷。 随意,如果你需要更多的信息发表评论。



文章来源: Where does bytecode injection happen?