【问题标题】:Where does bytecode injection happen?字节码注入发生在哪里?
【发布时间】:2019-04-04 05:19:32
【问题描述】:

动机

我有一个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   

【问题讨论】:

  • 这是一个非常有趣的问题@ipavlic!如果有人可以提供有关 Spring + AOP 内部的文献链接,将不胜感激。我想编写自己的注入代码的 Spring 注释。

标签: annotations classloader bytecode bytecode-manipulation


【解决方案1】:

是的,可以让您的自定义类加载器加载一个类,并通过字节码操作工具(如JavassistASM)执行修改,而不是将类文件中的字节码加载到内存中,而是将修改后的字节码加载到内存中一。 虽然有更简单(我认为更好)的方法。

注释处理器工具 (APT)

从 Java 6 开始,您拥有 APT,它允许您挂钩到编译过程(通过 javac 中的 -processor 参数)。使用 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

运行时修改

最后我们进行了运行时字节码修改。尽管您的想法是可能的,但我认为我会离开类加载器 magic 并使用 Javassist 之类的工具,它还允许您拥有可以修改的 dynamic proxiesreloaded

在 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 和后编译过程都允许您修改类公共接口)。我可以在这方面再多说一点,但我认为这已经足够让你深思了。如果您需要更多信息,请随时发表评论。

【讨论】:

    猜你喜欢
    • 2012-02-21
    • 1970-01-01
    • 2012-01-19
    • 2023-04-10
    • 2011-04-23
    • 2020-10-28
    • 1970-01-01
    • 2023-03-17
    • 2021-10-10
    相关资源
    最近更新 更多