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

Java & ByteBuddy:如何正确实现MethodDelegation方法拦截

解决ByteBuddy生成内部类子类时的NoSuchMethodException问题

问题原因

Person是Proxy类的非静态内部类,Java中这类内部类的构造方法会隐式包含一个外部类实例的参数(即使你声明的是无参构造)。ByteBuddy默认生成子类时,会尝试调用父类的无参构造,但实际上父类的构造需要外部类实例,生成的子类并没有对应的无参构造,因此抛出NoSuchMethodException。

解决方案

提供两种可行的修复方式:

方式1:将内部类改为静态类

把Person和Interceptor声明为static内部类,这样它们不再依赖外部类实例,构造方法也不会携带隐藏参数。修改后的完整代码:

package io.xxx.intercepter;

import java.lang.reflect.Method;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Empty;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperMethod;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;

public class Proxy {
  
  public static void main(String[] args) throws Exception {

    new ByteBuddy()
    .subclass(Person.class)
    .method(ElementMatchers.named("getJob")) // 精准匹配目标方法,避免拦截不必要的方法
    .intercept(MethodDelegation.to(Interceptor.class))
    .make()
    .load(Person.class.getClassLoader())
    .getLoaded()
    .getDeclaredConstructor()
    .newInstance()
    .getJob("engineer"); // 修正拼写错误:egnineer -> engineer
  }
  
  // 改为静态内部类
  public static class Person {
    
    private String work = "coding";
    
    public Person() {}
    
    public String getJob(String job) {
      
      this.work = job;
      
      System.out.println("I'm the Person " + work);
      
      return work;
    }
  }

 // 改为静态内部类
 public static class Interceptor {
  
    @RuntimeType
    public static Object intercept(
        @This Object self, 
        @Origin Method method,
        @AllArguments Object[] args,
        @SuperMethod(nullIfImpossible = true) Method superMethod,
        @Empty Object defaultValue) throws Throwable {
  
      if(superMethod == null)
        return defaultValue;
      
      System.out.println("I'm the proxy --> " + method.getName());
 
      Object object = superMethod.invoke(self, args);
  
      return object;

    }
  }
}

方式2:显式指定子类构造并传入外部类实例

如果必须保留非静态内部类,需要在ByteBuddy中声明构造方法并传入外部类实例:

public static void main(String[] args) throws Exception {
    Proxy outerInstance = new Proxy(); // 创建外部类实例
    
    Person proxyInstance = new ByteBuddy()
    .subclass(Person.class)
    .defineConstructor(Visibility.PUBLIC)
    .withParameter(Proxy.class) // 声明构造参数为外部类实例
    .intercept(MethodCall.invoke(Person.class.getDeclaredConstructor(Proxy.class))
               .withArgument(0)) // 调用父类构造并传入外部类实例
    .method(ElementMatchers.named("getJob"))
    .intercept(MethodDelegation.to(Interceptor.class))
    .make()
    .load(Person.class.getClassLoader())
    .getLoaded()
    .getDeclaredConstructor(Proxy.class)
    .newInstance(outerInstance); // 传入外部类实例创建代理对象
    
    proxyInstance.getJob("engineer");
}

额外优化建议

  • 匹配方法时优先使用ElementMatchers.named("getJob")代替ElementMatchers.any(),避免拦截构造方法等不必要的方法;
  • 修正代码中的拼写错误:egnineer改为engineer。

内容的提问来源于stack exchange,提问作者stackoverflow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 15:45:30