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
相关产品推荐
相关产品推荐

