我想拦截所有使用@Inject注释的方法。以下测试表明它与方法一起工作正常,但它不适用于构造函数。我错过了什么?
我尝试添加自定义方法匹配器,我注意到我从未获得与构造函数对应的MethodDescription。
public class InterceptConstructorTest {
@Test
public void testConstructorInterception() {
ByteBuddyAgent.install();
new AgentBuilder.Default().type(nameStartsWith("test")).transform(new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription td) {
return builder.method(isAnnotatedWith(Inject.class))
.intercept(MethodDelegation.to(MethodInterceptor.class).andThen(SuperMethodCall.INSTANCE));
}
}).installOnByteBuddyAgent();
// Call constructor => NOT intercepted
MyClass myClass = new MyClass("a param");
// Call method => intercepted
myClass.aMethod("a param");
}
}
class MyClass {
@Inject
public MyClass(String aParam) {
System.out.println("constructor called");
}
@Inject
public void aMethod(String aParam) {
System.out.println("aMethod called");
}
}
class MethodInterceptor {
public static void intercept(@Origin Method method) {
System.out.println("Intercepted: " + method.getName());
}
}
输出:
constructor called
Intercepted: aMethod
aMethod called
答案 0 :(得分:3)
您明确指定您只想拦截方法:
builder.method(isAnnotatedWith(Inject.class))
你可以这样做:
builder.constructor(isAnnotatedWith(Inject.class))
甚至:
builder.invokeable(isAnnotatedWith(Inject.class))
然而,这是一个问题。任何构造函数必须从被截获的构造函数中调用另一个构造函数。在您的情况下,已经使用SuperMethodCall.INSTANCE
给出了这个,您的代码将会运行。但是要小心一些构造不能用于构造函数,例如,在调用超级构造函数之前,不能注入@This
引用。如果合适,您可以例如切换:
MethodDelegation.to(MethodInterceptor.class)
.andThen(SuperMethodCall.INSTANCE)
成为
SuperMethodCall.INSTANCE
.andThen(MethodDelegation.to(MethodInterceptor.class))
使用此排序时,如果注入截获实例的属性,JVM不会再抱怨。
最后,请确保提供适当的拦截:
class MethodInterceptor {
public static void intercept(@Origin Method method) {
System.out.println("Intercepted: " + method.getName());
}
public static void intercept(@Origin Constructor<?> constructor) {
System.out.println("Intercepted: " + constructor.getName());
}
}
否则,Byte Buddy无法将Constructor
引用绑定到Method
并将该方法从绑定中丢弃(因为0.7.6之前,存在导致验证程序错误的错误。)使用Java 8时,您还可以使用Executable
类型提供单个拦截器。