2015-12-09 89 views
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 

回答

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()); 
    } 
} 

否则,字节好友不能绑定一个Constructor参照Method和绑定丢弃方法(从0.7.6之前,有一个导致验证错误的错误)。使用Java 8时,您也可以使用Executable类型提供一个拦截器。

+0

我试着用“构造函数”和“invokeable”,我得到以下例外(版本0.7.5):http://pastebin.com/peUnSdDw – user3408654

+1

您正在使用@Origin和Method类型,其中Constructor应该是用过的。 Byte Buddy应该知道,它是一个bug。你需要使用Constructor作为该拦截器的类型。 –

+0

哦,对,谢谢!现在我更好地理解异常消息。我不知道反射API在构造函数和方法之间做了区分。在构建器上有方法/构造器/可调用也更有意义。 – user3408654