ByteBuddy代理接口

7 java reflection bytecode byte-buddy

我正在尝试将Cglib代理转换为ByteBuddy.Cglib有net.sf.cglib.proxy.Proxy接口来拦截所有方法调用.我检查了ByteBuddy的文档但是找不到这样的例子.如果我没有用ByteBuddy实例化的每个对象都有这样的接口,我会再次重复同样的事情并且agin.使用ByteBuddy有更好的方法吗?

这是我的示例代码段:

服务:

public class MyService {

    public void sayFoo() {
        System.out.println("foo");
    }

    public void sayBar() {
        System.out.println("bar");
    }
}
Run Code Online (Sandbox Code Playgroud)

拦截器:

public class MyServiceInterceptor {

    public void sayFoo(@SuperCall Callable<Void> zuper) {
        try {
            zuper.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sayBar(@SuperCall Callable<Void> zuper) {
        try {
            zuper.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.MethodDelegation;
import net.bytebuddy.instrumentation.method.matcher.MethodMatchers;

public class Main {

    public static void main(String[] args) throws Exception {
        ByteBuddy buddy = new ByteBuddy(ClassFileVersion.forCurrentJavaVersion());
        Class<? extends MyService> serviceClass =
                buddy.subclass(MyService.class)
                .method(MethodMatchers.named("sayFoo").or(MethodMatchers.named("sayBar")))
                .intercept(MethodDelegation.to(new MyServiceInterceptor()))
                .make()
                .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded();

        MyService service = serviceClass.newInstance();

        service.sayFoo();
        service.sayBar();
    }
}
Run Code Online (Sandbox Code Playgroud)

Raf*_*ter 11

如果可能的话,Byte Buddy会查看任何可能的目标方法并将其绑定.如果有多个可能的目标方法,它将绑定最具体的目标方法,或者如果它不明确则抛出异常.在您的示例中,绑定将是不明确的,但是当您将拦截器方法(in MyServiceInterceptor)命名为与截获的方法(in )完全相同时Service,Byte Buddy认为使用相同名称的拦截器方法拦截每个方法可能就是您想要做的.正如所提到的javadoc中的MethodInterceptor字节将好友:

  1. 找到它可以绑定的任何方法并丢弃其他方法.
  2. 检查方法是否注释,@BindingPriority并选择具有最高优先级的方法.
  3. 如果存在至少一个方法,则截取与截获方法具有相同名称的方法.
  4. 如果使用@Argument注释,请选择具有最具体参数类型的方法,并类似于Java编译器来识别最具体的绑定,以识别重载方法调用的目标.
  5. 采用具有最多参数的方法.

您还可以通过添加/删除AmbiguityResolvers来更改此默认行为.

如果要指定一个拦截器方法,该方法能够拦截任何具有超级方法的方法,则可以编写以下拦截器类:

public class MyServiceInterceptor {
  @RuntimeType
  public static Object intercept(@SuperCall Callable<?> zuper) throws Exception {
    return zuper.call();
  }
}
Run Code Online (Sandbox Code Playgroud)

该方法的名称无关紧要,Byte Buddy将绑定拦截器,因为它是唯一可能的目标.您需要添加@RuntimeType注释,因为@SuperCall代理返回Object并且Byte Buddy需要在截获的方法中强制转换(并且可能取消装箱)该值.

使用这个拦截器(请注意,该方法也是static这样,Byte Buddy不需要添加用于保存实例的字段MyServiceInterceptor),您可以简单地写:

public class Main {
  public static void main(String[] args) throws Exception {
    Class<? extends MyService> serviceClass = new ByteBuddy()
      .subclass(MyService.class)
      .method(ElementMatchers.named("sayFoo").or(ElementMatchers.named("sayBar")))
      .intercept(MethodDelegation.to(MyServiceInterceptor.class))
      .make()
      .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
      .getLoaded();

    MyService service = serviceClass.newInstance();

    service.sayFoo();
    service.sayBar();
  }
}
Run Code Online (Sandbox Code Playgroud)

你会得到理想的结果.如示例所示,您可以编写

new ByteBuddy();
Run Code Online (Sandbox Code Playgroud)

代替

new ByteBuddy(ClassFileVersion.forCurrentJavaVersion());
Run Code Online (Sandbox Code Playgroud)

这是一回事.

Byte Buddy不使用任何接口,因为它希望避免生成的类依赖于任何Byte Buddy类.这样做,您可以重用您生成的类,即使在ClassLoader不知道Byte Buddy依赖项的情况下加载它们也是如此.