如何拦截构造函数

时间:2015-12-09 11:11:25

标签: java bytecode byte-buddy

我想拦截所有使用@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

1 个答案:

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