2015-12-11 54 views
2

我試圖攔截與@Inject註釋構造函數。在小單元測試的情況下,這很好。但是,在像Spring這樣的DI容器的情況下,它會以ClassNotFoundException失敗。攔截的構造導致ClassNotFoundException的

我設法縮小的根本原因。在儀表類上調用getDeclaredConstructors將觸發此異常。有趣的是,如果我們首先創建該類的一個實例,問題就會消失。

例如:

public class InterceptConstructorTest { 

    @Test 
    public void testConstructorInterception() throws ClassNotFoundException { 

     ByteBuddyAgent.install(); 

     new AgentBuilder.Default().type(nameStartsWith("test")).transform(new AgentBuilder.Transformer() { 

      @Override 
      public Builder<?> transform(Builder<?> builder, TypeDescription td) { 

       return builder.constructor(isAnnotatedWith(Inject.class)) 
         .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(ConstructorInterceptor.class))); 
      } 
     }).installOnByteBuddyAgent(); 

     // If this line is uncommented, ClassNotFoundException won't be thrown 
//  MyClass myClass = new MyClass("a param"); 

     // Manually load MyClass 
     Class<?> myClassDefinition = getClass().getClassLoader().loadClass("test.MyClass"); 

     // Throws NoClassDefFoundError 
     for(Constructor<?> constructor : myClassDefinition.getDeclaredConstructors()) { 
      System.out.println(constructor); 
     } 
    } 
} 

堆棧堆棧跟蹤,可以發現:http://pastebin.com/1zhx3fVX

class MyClass { 

    @Inject 
    public MyClass(String aParam) { 
     System.out.println("constructor called"); 
    } 
} 

class ConstructorInterceptor { 

    public static void intercept() { 
     System.out.println("Intercepted"); 
    } 
} 
+1

自0.7.7版本,字節巴迪的新的默認'InitiailizationStrategy'需要照顧的問題給你。新版本目前與Maven Central Repository同步。 –

+0

在移至0.7.7之後,我的應用程序過早退出,執行'main'之前。沒有例外被提出。任何想法,我可以如何幫助你找出這個問題? – user3408654

+0

即使儀器失效,也不應該發生這種情況。您可以隨時添加'AgentBuilder.Listener'來檢查Byte Buddy是否發出錯誤。也許您的代理仍在使用老版本的Byte Buddy導致'NoClassDefFoundError'?你的單元測試是否適用於新版本?如果是這樣,我認爲你有一個版本衝突的地方。 –

回答

1

在這種情況下的問題是構造注射。爲了變基構造,字節好友需要創建一個額外的類型,並創建類似下面的一類:

class MyClass { 

    private synthetic MyClass(String aParam, $SomeType ignored) { 
     System.out.println("constructor called"); 
    } 

    @Inject 
    public MyClass(String aParam) { 
     this(aParam, null); 
     // Instrumentation logic. 
    } 
} 

附加型遺憾,必須進行重訂基期的構造函數創建一個獨特的簽名。有了方法,字節好友可以,而更改名稱,但對於構​​造函數是不可能的,因爲他們必須命名的類文件<init>被確認爲建設者。

Byte Buddy嘗試僅在之後加載輔助類某種類型已插裝。根據虛擬機,加載引用另一個類的類會導致引用類型的加載。如果這種類型是儀表類,則儀表放棄正在進行的循環儀表。

因此,字節的好友可以確保任何輔助類型在第一可能的點僅加載之後可以肯定的是,儀器類型被加載。它通過將自我初始化添加到工具類的類初始化程序中來實現此目的。在某種程度上,字節好友添加塊:

static { 
    ByteBuddy.loadAuxiliaryTypes(MyClass.class); 
} 

如果該塊沒有反映在課前執行,輔助類型不加載和您遇到異常被拋出。如果你叫:

Class.forName("test.MyClass", true, getClass().getClassLoader()); 

,而不是loadClass,不會在第二個參數表示熱切執行類初始化出現問題。另外,如果創建實例,則會執行初始化程序。

當然,這是不能令人滿意的,我現在加入一些邏輯來對輔助類型決定是否可以在儀表期間被加載,以避免這樣的錯誤。