2015-12-09 90 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