如何拦截构造函数

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 类型提供单个拦截器。