2012-05-22 48 views
2

我有一種情況,Guice正在爲某些綁定工作,而不是爲其他人工作。很明顯,我錯誤地使用了API。Guice不會攔截帶註釋的方法

部分原因可能是因爲我試圖讓我對Guice的使用過於「迷戀」。我創建了一個Module的繼承樹,我認爲我爲了自己的利益已經太聰明瞭(或愚蠢!)。

你看看下面的代碼之前,就請理解我的用意,這是提供一個可重用的Module,我可以在一個JAR放置並在多個項目共享。這個抽象的,可重用的Module將提供所謂的「默認綁定」,任何實現的Module都會自動頒發。事情是這樣叫Profiler的AOP MethodInterceptor,它看起來與@Calibrated註解的方法,並自動記錄多長時間需要爲這個方法來執行,等等。

注意以下事項:

@Target({ ElementType.METHOD }) 
@RetentionPolicy(RetentionPolicy.RUNTIME) 
@BindingAnnotation 
public @interface Calibrated{} 

public class Profiler implement MethodInterceptor { 
    @Override 
    public Object invoke(MethodInvocation arg0) throws Throwable { 
     // Eventually it will calculate and log the amount of time 
     // it takes an intercepted method to execute, hence "Profiler". 
     System.out.println("Intercepted!"); 
     return arg0.proceed(); 
    } 
} 

public abstract class BaseModule implements Module { 
    private Binder binder; 

    public BaseModule() { 
     super(); 
    } 

    public abstract void bindDependencies(); 

    @Override 
    public void configure(Binder bind) { 
     // Save the binder Guice passes so our subclasses can reuse it. 
     setBinder(bind); 

     // TODO: For now do nothing, down the road add some 
     // "default bindings" here. 

     // Now let subclasses define their own bindings. 
     bindDependencies(); 
    } 

    // getter and setter for "binder" field 
    // ... 
} 

public abstract class AbstractAppModule extends BaseModule { 
    /* Guice Injector. */ 
    private Injector injector; 

    // Some other fields (unimportant for this code snippet) 

    public AbstractAppModule() { 
     super(); 
    } 

    // getters and setters for all fields 
    // ... 

    public Object inject(Class<?> classKey) { 
     if(injector == null) 
      injector = Guice.createInjector(this); 

     return injector.getInstance(classKey); 
    } 
} 

因此,要使用這個小型圖書館:

public class DummyObj { 
    private int nonsenseInteger = -1; 

    // getter & setter for nonsenseInteger 

    @Calibrated 
    public void shouldBeIntercepted() { 
     System.out.println("I have been intercepted."); 
    } 
} 

public class MyAppModule extends AbstractAppModule { 
    private Profiler profiler; 

    // getter and setter for profiler 

    @Override 
    public void bindDependencies() { 
     DummyObj dummy = new DummyObj(); 
     dummy.setNonsenseInteger(29); 

     // When asked for a DummyObj.class, return this instance. 
     getBinder().bind(DummyObj.class).toInstance(dummy); 

     // When a method is @Calibrated, intercept it with the given profiler. 
     getBinder().bindInterceptor(Matchers.any(), 
      Matchers.annotatedWith(Calibrated.class), 
      profiler); 
    } 
} 

public class TestGuice { 
    public static void main(String[] args) { 
     Profiler profiler = new Profiler(); 
     MyAppModule mod = new MyAppModule(); 
     mod.setProfiler(profiler); 

     // Should return the bounded instance. 
     DummyObj dummy = (DummyObj.class)mod.inject(DummyObj.class); 

     // Should be intercepted... 
     dummy.shouldBeIntercepted(); 

     System.out.println(dummy.getNonsenseInteger()); 
    } 
} 

這是一個很大的代碼,以便在鍵入時,這一切也許是我介紹了幾個錯別字,但我向你保證,這個代碼編譯和運行時沒有拋出異常。

這裏發生了什麼:

  • @Calibrated shouldBeIntercepted()方法是攔截;但...
  • 控制檯輸出顯示虛擬的無意義整數爲... 29 !!!!

所以,無論多麼貧窮設計你可能認爲這是,你不能說吉斯確實是1結合工作(實例綁定),而不是AOP方法攔截。

如果實例綁定不起作用,那麼我會很高興地重新訪問我的設計。但其他事情正在發生。我想知道我的繼承樹是否拋棄了Binder

而且我已經驗證了我正確地綁定攔截器的註解:我創建了另一個包,我只是執行(而不是這個繼承樹)Module並使用相同的註解,同樣Profiler,並且它完美精細。

我用Injector.getAllBindings()打印出我的所有MyAppModule的綁定作爲字符串的地圖。沒有任何東西可以作爲這個錯誤的明確來源。

任何想法?

回答

6

攔截僅適用於由Guice創建的對象(請參閱「限制」http://code.google.com/p/google-guice/wiki/AOP#Limitations)。您正在使用「new」創建DummyObj,因此無論您的模塊設置得有多聰明,該實例都會在Guice外創建。

這裏有一個基於你的編碼的小snipplet。 (我使用校準註釋,但是其他所有內容都在一個類中,您應該查看「AbstractModule」。它可以節省您在模塊層次結構中執行的大量手動操作。

public class MyModule extends AbstractModule implements MethodInterceptor { 

@Override 
public Object invoke(MethodInvocation methodInvocation) throws Throwable { 

    System.out.println("[email protected]!"); 

    return methodInvocation.proceed(); 
} 

@Override 
protected void configure() { 
    bind(Integer.class).annotatedWith(Names.named("nonsense")).toInstance(29); 
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(Calibrated.class), this); 
} 

public static void main(String... args) { 
    Dummy dummy = Guice.createInjector(new MyModule()).getInstance(Dummy.class); 

    dummy.doSomething(); 

    System.out.println(dummy.getNonsense()); 
} 
} 

我的假人:

public class Dummy { 

@Inject 
@Named("nonsense") 
private int nonsense; 


public int getNonsense() { 
    return nonsense; 
} 


public void setNonsense(int nonsense) { 
    this.nonsense = nonsense; 
} 

@Calibrated 
public void doSomething() { 
    System.out.println("I have been intercepted!"); 
} 
} 

所以,你看到了什麼?我從來沒有使用「新」這個詞(Module除外)。我讓Guice處理Dummy-Object,然後爲無意義的int配置值,然後將其注入。

輸出:

[email protected]! 
I have been intercepted! 
29 
+0

所以是你在說什麼:(1)因爲我使用'new',我實際上沒有獲得通過吉斯的'Dummy';和(2)因爲我然後從非Guice注入的'Dummy'運行'Calibrated'方法,那Guice沒有攔截它?這是主意嗎?如果是這樣,那就很有道理。如果不是,請你澄清一下嗎?再次感謝您的幫助和這麼好的回答(+1)! – IAmYourFaja

+0

您的DummyObj實例不是由guice創建的。它由你創建並且在模塊中綁定,所以你總是會得到相同的預創建的實例。所以基本上:是的,我說的是(1)和(2)。它的理解有點棘手,請閱讀下面的鏈接(上面的鏈接)。不要問我爲什麼這樣做......這是一個很好的最佳實踐,儘可能避免「新」。 –