ByteBuddy 实战 2 实例方法插桩

171 阅读2分钟

插桩概念解释

插桩:一般指字节码插桩,就是对字节码进行修改或增强,以监控、分析或修改程序的行为。

拦截指定方法

@Test
public void makeClassTest() throws IOException, InstantiationException, IllegalAccessException {
    DynamicType.Unloaded<Object> unloaded = new ByteBuddy()
            .subclass(Object.class) // 继承自 Object 类
            .name("com.example.DynamicGeneratedClass") // 指定类名
            .method(ElementMatchers.named("toString")) //通过名字拦截指定方法
            //指定拦截到方法后 如何处理 。拦截器是什么
            .intercept(FixedValue.value("hell bytebuddy!"))
            .make();// 创建类型
    //把字节码文件加载到jvm
    DynamicType.Loaded<Object> load = unloaded.load(getClass().getClassLoader());
    //获取到class对象
    Class<?> loaded = load.getLoaded();
    //方法调用
    Object obj = loaded.newInstance();
    obj.toString();
    unloaded.saveIn(new File(path));
}

bytebuddy 动态增强的三种方式

Byte Buddy 是一个强大的 Java 库,用于在运行时生成和操作 Java 字节码。它提供了多种方式来进行动态增强,主要包括以下三种方式:

  1. 子类化(Subclassing)

    • 通过创建目标类的子类来增强类的功能。这种方式适用于需要添加或重写类的方法的场景。
    • 可以通过 ByteBuddy.subclass() 方法创建一个类的子类,并在其中添加新的方法或重写现有方法。
    Class<?> dynamicType = new ByteBuddy()
        .subclass(SomeClass.class)
        .method(ElementMatchers.named("someMethod"))
        .intercept(MethodDelegation.to(SomeInterceptor.class))
        .make()
        .load(SomeClass.class.getClassLoader())
        .getLoaded();
    
  2. 重定义(Redefinition)

    • 直接修改现有类的字节码。这种方式不创建子类,而是直接在原类上进行修改,适合需要在运行时修改类行为的场景。
     @Test
     public void test2() throws IOException {
         DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
                 .redefine(UserManager.class)// 继承自 Object 类
                 .name("a.b.SubObj")
                 .method(ElementMatchers.named("selectUserName")) //通过名字拦截指定方法
                 //指定拦截到方法后 如何处理 。拦截器是什么
                 .intercept(FixedValue.value("hell bytebuddy!"))
                 .make();// 创建类型
         unloaded.saveIn(new File(path));
     }
    
  3. 变基(rebase)

    • 保留原方法并重命名为xxoriginaloriginalkqg3ADs3,xx为拦截后的逻辑。
     @Test
     public void test2() throws IOException {
         DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
                 .rebase(UserManager.class)// 继承自 Object 类
                 .name("a.b.SubObj")
                 .method(ElementMatchers.named("toString")) //通过名字拦截指定方法
                 //指定拦截到方法后 如何处理 。拦截器是什么
                 .intercept(FixedValue.value("hell bytebuddy!"))
                 .make();// 创建类型
         unloaded.saveIn(new File(path));
     }
    

通过字节码文件可以查看到: image.png

插入新方法

@Test
public void test2() throws IOException {
    DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
            .redefine(UserManager.class)// 继承自 Object 类
            .name("a.b.SubObj")
            //定义方法的名字 返回值 修饰符
            .defineMethod("selectUserName2",String.class, Modifier.PUBLIC+Modifier.STATIC)
            .withParameter(String.class,"param1")//自定方法参数
            .intercept(FixedValue.value("bytebuddy 生成的新方法!"))//方法体
            .make();// 创建类型
    unloaded.saveIn(new File(path));
}

生成效果: image.png

下一篇:# ByteBuddy 实战 3 自定义拦截逻辑
参考:
# bytebuddy核心教程