我正在尝试将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();
    }
}

最佳答案

如果可能,Byte Buddy将查看任何可能的目标方法并将其绑定。如果有多个可能的目标方法,它将绑定最具体的目标方法,或者如果模棱两可,则抛出异常。在您的示例中,绑定将是模棱两可的,但是当您将拦截器方法(在MyServiceInterceptor中)命名为与拦截方法(在Service中)相同时,Byte Buddy认为使用同名的拦截器方法拦截每个方法可能就是您想要的做。如javadoc of the MethodInterceptor 字节好友中所述:

  • 查找可以绑定并丢弃其他方法的任何方法。
  • 检查方法是否用@BindingPriority注释,并选择优先级最高的方法。
  • 与至少有一个与被拦截方法同名的方法进行拦截。
  • 如果使用@Argument批注,则选择具有最特定参数类型的方法,并以与Java编译器类似的方式解析最特定的绑定,以标识重载方法调用的目标。
  • 采用参数最多的方法。

  • 您还可以通过添加/删除 AmbiguityResolver s来更改此默认行为。

    如果要指定一个能够拦截的拦截器方法,则任何具有超级方法的方法都可以编写以下拦截器类:
    public class MyServiceInterceptor {
      @RuntimeType
      public static Object intercept(@SuperCall Callable<?> zuper) throws Exception {
        return zuper.call();
      }
    }
    

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

    您会得到理想的结果。如示例所示,您可以编写
    new ByteBuddy();
    

    代替
    new ByteBuddy(ClassFileVersion.forCurrentJavaVersion());
    

    是同一回事。

    字节伙伴不使用任何接口,因为它希望避免生成的类对任何字节伙伴类的依赖。这样做,即使使用未知的Byte Buddy依赖项加载ClassLoader时,也可以重用生成的类。

    07-24 18:29