我正在尝试将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");
}
}
拦截器:
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();
}
}
答案 0 :(得分:10)
Byte Buddy将查看任何可能的目标方法并将其绑定(如果可能的话)。如果有多个可能的目标方法,它将绑定最具体的目标方法,或者如果它不明确则抛出异常。在您的示例中,绑定将是不明确的,但是当您将拦截器方法(在MyServiceInterceptor
)中命名为截获方法(在Service
中)时,Byte Buddy认为使用拦截器方法拦截每个方法相同的名字可能就是你想要做的。正如javadoc of the MethodInterceptor
Byte Buddy所说:
@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不使用任何接口,因为它希望避免生成的类依赖于任何Byte Buddy类。这样做,即使使用不知道Byte Buddy依赖性的ClassLoader
加载它们,也可以重用您生成的类。