You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过ByteBuddy将同步方法改为异步?解决循环调用问题

问题

尝试通过ByteBuddy将同步方法转为异步时,出现performTask循环调用的问题,代码如下:

public class ByteBuddyExample {
    public static class Foo {
        public void performTask() {
            System.out.println("Performing task...");
        }
    }

    public static class AsyncAdvice {
        @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
        public static boolean enter(@Advice.This Foo foo, @Advice.Origin Method method) {
            CompletableFuture.runAsync(new Runnable() {
                @Override
                public void run() {
                    runAsyncMethod(foo, method);
                }
            });
            return true;
        }

        public static void runAsyncMethod(Foo foo, Method method) {
            try {
                System.out.println("Async task started...");
                method.invoke(foo);
                System.out.println("Async task completed.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        ByteBuddyAgent.install();

        new ByteBuddy().rebase(Foo.class)
            .visit(Advice.to(AsyncAdvice.class).on(ElementMatchers.named("performTask")))
            .make()
            .load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
            .saveIn(new File("./"));

        Foo foo = new Foo();
        foo.performTask();

        Thread.sleep(2000);
    }
}

已知ByteBuddy的rebase方法会将原方法改为私有并重命名,生成新的performTask方法,但保存字节码后发现原方法被修改,导致循环调用。请问如何解决该问题,正确实现同步方法转异步的需求?

解决方案

问题根源

使用@Advice.Origin Method method获取到的是修改后的新performTask方法,而非被rebase后重命名的原方法。调用method.invoke(foo)时会再次触发Advice逻辑,形成无限循环。

解决思路

  1. 获取原方法的真实引用:通过@Advice.Origin("#m")获取原方法名称,再通过反射找到ByteBuddy重命名后的私有原方法(默认后缀为$original)。
  2. 直接调用原方法逻辑:调用重命名后的原方法,避免触发新方法的Advice拦截。
  3. 保留Advice返回值:返回true跳过原方法的同步执行逻辑,确保只有异步线程执行任务。

修改后的完整代码

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.reflect.Method;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class ByteBuddyExample {
    public static class Foo {
        public void performTask() {
            System.out.println("Performing task...");
        }
    }

    public static class AsyncAdvice {
        @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
        public static boolean enter(@Advice.This Foo foo, @Advice.Origin("#m") String methodName) {
            CompletableFuture.runAsync(() -> {
                try {
                    // 获取ByteBuddy重命名后的原方法(默认后缀为$original)
                    Method originalMethod = Foo.class.getDeclaredMethod(methodName + "$original");
                    originalMethod.setAccessible(true);
                    System.out.println("Async task started...");
                    originalMethod.invoke(foo);
                    System.out.println("Async task completed.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            // 返回true跳过原方法的同步执行流程
            return true;
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        ByteBuddyAgent.install();

        new ByteBuddy().rebase(Foo.class)
                .visit(Advice.to(AsyncAdvice.class).on(ElementMatchers.named("performTask")))
                .make()
                .load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
                .saveIn(new File("./"));

        Foo foo = new Foo();
        foo.performTask();

        Thread.sleep(2000);
    }
}

补充说明

  • 如果自定义了原方法的重命名规则,需将代码中的$original替换为自定义后缀。
  • 若使用redefine而非rebase,原方法会被直接覆盖,这种场景下需要预先保存原方法逻辑,因此rebase更适合保留原方法的需求场景。

内容的提问来源于stack exchange,提问作者阮久卿

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 02:15:04