使用 ByteBuddy 拦截未实现的接口方法
Intercepting unimplemented interface method with ByteBuddy
让我们有一个简单的界面:
public interface Person {
String getFirstName();
void setFirstName(String firstName);
}
现在我这样创建代理:
public class TestClass {
@Test
public void testMethod() throws InstantiationException, IllegalAccessException {
final Class<?> proxy = new ByteBuddy().subclass(Object.class).implement(Person.class)
// AND INTERCEPTS ALL ABSTRACT AND OBJECT DEFAULT METHODS
// AND TRAP ALL METHODS EXCEPT CONSTRUCTORS AND FINALIZER
.method(
ElementMatchers.isAbstract()
)
// AND DELEGATE CALL TO OUR INVOCATION HANDLER STORED IN PRIVATE FIELD OF THE CLASS
.intercept(MethodDelegation.to(Delegator.class))
// NOW CREATE THE BYTE-CODE
.make()
// AND LOAD IT IN CURRENT CLASSLOADER
.load(TestClass.class.getClassLoader())
// RETURN
.getLoaded();
final Person theInstance = (Person) proxy.newInstance();
theInstance.setFirstName("ABC");
theInstance.getFirstName();
}
public static class Delegator {
@RuntimeType
public static Object intercept(@This Object proxy, @Origin Method method, @AllArguments Object[] args) throws Throwable {
System.out.println("Intercept A: " + proxy + ", " + method + ", " + args);
return null;
}
@RuntimeType
public static Object intercept(@This Object proxy, @Origin Method method) throws Throwable {
System.out.println("Intercept B: " + proxy + ", " + method);
return null;
}
@RuntimeType
public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod) throws Throwable {
System.out.println("Intercept C: " + proxy + ", " + defaultMethod + ", " + superMethod);
return null;
}
@RuntimeType
public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod, @AllArguments Object[] args) throws Throwable {
System.out.println("Intercept D: " + proxy + ", " + defaultMethod + ", " + superMethod + ", " + args);
return null;
}
}
}
当我执行测试时,我看到这个输出:
Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null
Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null
如果调用方法源自接口并且不是由 superclass 实现,我似乎永远无法访问被调用方法的方法签名?!
此外,我希望当我调用 theInstance.setFirstName("ABC");
时,我会看到 Intercept D:
也接受所有输入参数。
我在这里想念什么?我阅读了文档,但我仍然感到困惑。
一般来说,我想创建 Delegator
来捕获代理 class 中的所有方法并处理以下情况:
- 做一些事情然后传播对原始方法的调用
- 做点什么return它自己的结果而不调用原始方法
为此,我需要访问:
- 代理实例
- 调用的方法
- 参数
- 如何调用原始方法体的方法(如果它不是抽象的)
如何实现?
Auch - 问题是某些注释在不同包的 ByteBuddy 中声明了两次 - 例如:
net.bytebuddy.implementation.bind.annotation.AllArguments
net.bytebuddy.asm.Advice.AllArguments
当使用来自 implementation
包的注解时,一切正常。这是一个令人讨厌的陷阱!
让我们有一个简单的界面:
public interface Person {
String getFirstName();
void setFirstName(String firstName);
}
现在我这样创建代理:
public class TestClass {
@Test
public void testMethod() throws InstantiationException, IllegalAccessException {
final Class<?> proxy = new ByteBuddy().subclass(Object.class).implement(Person.class)
// AND INTERCEPTS ALL ABSTRACT AND OBJECT DEFAULT METHODS
// AND TRAP ALL METHODS EXCEPT CONSTRUCTORS AND FINALIZER
.method(
ElementMatchers.isAbstract()
)
// AND DELEGATE CALL TO OUR INVOCATION HANDLER STORED IN PRIVATE FIELD OF THE CLASS
.intercept(MethodDelegation.to(Delegator.class))
// NOW CREATE THE BYTE-CODE
.make()
// AND LOAD IT IN CURRENT CLASSLOADER
.load(TestClass.class.getClassLoader())
// RETURN
.getLoaded();
final Person theInstance = (Person) proxy.newInstance();
theInstance.setFirstName("ABC");
theInstance.getFirstName();
}
public static class Delegator {
@RuntimeType
public static Object intercept(@This Object proxy, @Origin Method method, @AllArguments Object[] args) throws Throwable {
System.out.println("Intercept A: " + proxy + ", " + method + ", " + args);
return null;
}
@RuntimeType
public static Object intercept(@This Object proxy, @Origin Method method) throws Throwable {
System.out.println("Intercept B: " + proxy + ", " + method);
return null;
}
@RuntimeType
public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod) throws Throwable {
System.out.println("Intercept C: " + proxy + ", " + defaultMethod + ", " + superMethod);
return null;
}
@RuntimeType
public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod, @AllArguments Object[] args) throws Throwable {
System.out.println("Intercept D: " + proxy + ", " + defaultMethod + ", " + superMethod + ", " + args);
return null;
}
}
}
当我执行测试时,我看到这个输出:
Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null
Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null
如果调用方法源自接口并且不是由 superclass 实现,我似乎永远无法访问被调用方法的方法签名?!
此外,我希望当我调用 theInstance.setFirstName("ABC");
时,我会看到 Intercept D:
也接受所有输入参数。
我在这里想念什么?我阅读了文档,但我仍然感到困惑。
一般来说,我想创建 Delegator
来捕获代理 class 中的所有方法并处理以下情况:
- 做一些事情然后传播对原始方法的调用
- 做点什么return它自己的结果而不调用原始方法
为此,我需要访问:
- 代理实例
- 调用的方法
- 参数
- 如何调用原始方法体的方法(如果它不是抽象的)
如何实现?
Auch - 问题是某些注释在不同包的 ByteBuddy 中声明了两次 - 例如:
net.bytebuddy.implementation.bind.annotation.AllArguments
net.bytebuddy.asm.Advice.AllArguments
当使用来自 implementation
包的注解时,一切正常。这是一个令人讨厌的陷阱!