如何拦截构造函数
How to intercept a constructor
我想拦截所有用@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
您明确指定只想拦截方法:
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
类型提供单个拦截器。
我想拦截所有用@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
您明确指定只想拦截方法:
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
类型提供单个拦截器。