7

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

这是我的示例代码片段:

服务:

public class MyService {

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

    public void sayBar() {
        System.out.println("bar");
    }
}

拦截器:

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();
        }
    }
}

测试:

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();
    }
}
4

1 回答 1

12

如果可能的话,Byte Buddy 将查看任何可能的目标方法并绑定它。如果有多个可能的目标方法,它将绑定最具体的一个,如果不明确则抛出异常。在您的示例中,绑定将是模棱两可的,但是当您将拦截器方法 (in ) 命名为与拦截方法 (in MyServiceInterceptor) 相同时Service,Byte Buddy 认为使用相同名称的拦截器方法拦截每个方法可能是您想要做的。正如Byte Buddy的javadocMethodInterceptor提到的那样:

  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();
  }
}

方法的名称无关紧要,Byte Buddy 将绑定拦截器,因为它是唯一可能的目标。您需要添加@RuntimeType注释,因为@SuperCall代理返回 anObject并且 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();
  }
}

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

new ByteBuddy();

代替

new ByteBuddy(ClassFileVersion.forCurrentJavaVersion());

这是同样的事情。

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

于 2014-08-27T17:40:26.127 回答