1

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

1 回答 1

3

您明确指定您只想拦截方法:

builder.method(isAnnotatedWith(Inject.class))

您可以类似地执行以下操作:

builder.constructor(isAnnotatedWith(Inject.class))

甚至:

builder.invokeable(isAnnotatedWith(Inject.class))

然而,这就是问题所在。任何构造函数都必须从被拦截的构造函数中调用另一个构造函数。在您的情况下,这已经通过 using 给出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 无法绑定对 a 的Constructor引用Method并从绑定中丢弃该方法(从 0.7.6 开始,之前存在一个导致验证器错误的错误。)使用 Java 8 时,您还可以提供单个拦截器使用Executable类型。

于 2015-12-10T08:29:37.847 回答