Java 字节码增强

337 阅读3分钟

静态字节码增强

AspectJ

动态字节码增强

ByteBuddy

网址:bytebuddy.net/#/

Byte Buddy 的 API 旨在让代码简洁且易于每个人理解。尽管如此,Byte Buddy 仍然是完全可定制的,甚至可以定义自定义字节代码。此外,API 被设计为尽可能非侵入性,因此 Byte Buddy 不会在其创建的类中留下任何痕迹。因此,生成的类可以存在,而无需类路径上的 Byte Buddy。

Byte Buddy 使用 Java 5 编写,但支持为任何 Java 版本生成类。Byte Buddy 是一个轻量级库,仅依赖于 Java 字节码解析器库 ASM的 API ,而 ASM 本身不需要任何进一步的依赖项

依赖

Byte Buddy 是在ASM 之上编写的,ASM 是一个成熟且经过良好测试的库,用于读取和编写已编译的 Java 类。为了允许高级类型操作,Byte Buddy 有意向其用户公开 ASM API。当然,直接使用 ASM 仍然是完全可选的,大多数用户很可能永远不需要它。做出这样的选择是为了让 Byte Buddy 的用户不受其更高级别功能的限制,而是可以在必要时毫不费力地实现自定义实现。

ASM 之前更改了其公共 API,但从库的版本 4 开始添加了 API 兼容性机制。为了避免与此类旧版本发生版本冲突,Byte Buddy 将 ASM 依赖项重新打包到自己的命名空间中。如果您想直接使用 ASM,请使用byte-buddy-dep 工件提供的 Byte Buddy 版本,该版本明确依赖于 ASM。执行此操作时,您必须将 Byte Buddy 和 ASM 重新打包到您的命名空间中以避免版本冲突。

@Test  
public void test1() throws InstantiationException, IllegalAccessException {  
    new ByteBuddy()  
        .subclass(Object.class)  // 子类
        .name("example.Type")  // 包:example,类名:Type
        .method(ElementMatchers.named("toString")) // 筛选指定的方法
        .intercept(FixedValue.value("Hello World!"))  // 修改方法体
        .make()  // 创建未被加载的动态类型
        .saveIn(new File("/tmp/example/1111"));  // 保存生产的class文件
}

反编译生成的class文件得到:

package example;  
  
public class Type {  
public java.lang.String toString() { /* compiled code */ }  
  
public Type() { /* compiled code */ }  
}

@Test  
public void test2() throws InstantiationException, IllegalAccessException {  
    String toString = new ByteBuddy()  
        .subclass(Object.class)  
        .name("example.Type")  
        .method(ElementMatchers.named("toString")).intercept(FixedValue.value("Hello World!"))  
        .make()  
        .load(getClass().getClassLoader())  
        .getLoaded()  
        .newInstance()  
        .toString();  
    System.out.println(toString);  
}

ASM

Javassist

该库附带一个编译器,它接受包含 Java 源代码的字符串,这些字符串在应用程序运行时被转换为 Java 字节代码。这是一个非常雄心勃勃的想法,原则上也是一个好主意,因为 Java 源代码显然是描述 Java 类的好方法。然而,Javassist 编译器在功能上无法与 javac 编译器相比,并且在动态组合字符串以实现更复杂的逻辑时很容易出错。此外,Javassist 还附带一个代理库,该库与 JCL 的代理实用程序类似,但允许扩展类且不限于接口。然而,Javassist 代理工具的范围在其 API 和功能上仍然受到同样的限制。

Cglib

Cglib 是在 Java 早期实现的,不幸的是它没有跟上 Java 平台的发展。尽管如此,cglib 仍然是一个相当强大的库,但它的发展变得相当模糊。出于这个原因,它的许多用户放弃了 cglib。

Java Proxy

Java 类库附带了一个代理工具包,允许创建实现给定接口的类。这个内置的代理很方便,但也非常有限。