2016-09-20 101 views
2

我有一個叫StatsStore的接口。我有這個商店的2個實施。一個名爲InMemoryStatsStoreSqlStatsStore的內存和SQL實現。爲了注入它們,我創建了2個註釋@InMemoryStore@SqlStore。所述注射是:Guice多註釋

bind(StatsStore.class) 
    .annotatedWith(InMemoryStore.class) 
    .to(InMemoryStatsStore.class); 

bind(StatsStore.class) 
    .annotatedWith(SqlStore.class) 
    .to(SqlStatsStore.class); 

現在我要添加註釋的新層InMemoryStringStoreInMemoryNumberStore之間分開,但我不能多於一個註釋到結合線添加例如以下不會編譯:

bind(StatsStore.class) 
    .annotatedWith(InMemoryStore.class) 
    .annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well 
    .to(InMemoryNumberStore.class); 

如何添加超過一個註解更沒有使用一個單一的命名,而這樣做的層數越多我添加到它是相當複雜?所有

bind(StatsStore.class) 
    .annotatedWith(InMemoryStore.class) 
    .to(InMemoryStatsStore.class); 

bind(InMemoryStatsStore.class) 
    .annotatedWith(NumberStoreAnnotation.class) 
    .to(InMemoryNumberStore.class); 

感謝:

其他解決方案,我腦子裏想的是注射兩次。

回答

1

正如阿米特所說,你不能有多於@BindingAnnotation適用於任何給定的注射。在內部,Guice的工作方式類似於Map<Key, Provider>,其中Key是一個可能參數化的類,帶有一個可選的單個註釋實例。但是,因爲這些是實例,歡迎來到create your own instantiable annotation,它的工作方式爲Named

@Inject @InMemoryStore(NUMBER) StatsStore inMemoryNumberStore; 
@Inject @SqlStore(STRING) StatsStore sqlStringStore; 
// or 
@Inject @Store(dataType=NUMBER, backend=SQL) sqlNumberStore; 

註釋必須具有像這樣定義的字段。 (如果您有一個名爲value的元素,則可以省略屬性名稱per JLS 9.7.3。)等於註釋的定義爲as in the Annotation.equals docs

public enum DataType { NUMBER, STRING; } 
public enum Backend { SQL, IN_MEMORY; } 

@BindingAnnotation @Retention(SOURCE) @Target({ FIELD, PARAMETER, METHOD }) 
public @interface Store { 
    DataType dataType(); 
    Backend backend(); 
} 

這很好地工作了@Provides,當你可以調用註解你注入同樣的方式,但你怎麼能創造像Names.named實例的工廠方法?對於這一點,你需要做以下之一:

  1. Create an anonymous implementation,爲每個屬性訪問器以及對equalshashCode正確的實現。請注意,hashCode合同是much stricter than for Object,但您可以從Apache annotation utils或類似的庫中獲得兼容的實施。
  2. 使用AnnotationLiteral,其爲任意子類提供equalshashCode實現。
  3. 使用Google Auto或類似的代碼生成器爲您生成兼容實現的代碼。熟悉這種類型的解決方案對於Android和反射速度較慢的其他內存受限的環境特別有用,但這些環境通常會阻止您使用Guice。 (@Qualifier標註相同的方式工作在其他JSR-330兼容的依賴注入框架,不過,包括匕首。)

如果上面似乎有點複雜,或者如果你想比Guice的地圖更復雜的邏輯爲基礎的實現可以完成,一種替代方法是添加一個間接控制層:

public class StoreStore { 
    @Inject Provider<InMemoryNumberStore> inMemoryNumberStoreProvider; 
    // ... 
    // You can also inject the Injector to call getInstance with a class literal. 

    public StatsStore getStore(DataType dataType, Backend backend) { 
    // This can also be a switch or any other sort of lookup, of course. 
    if (dataType == NUMBER && backend == IN_MEMORY) { 
     return inMemoryNumberStoreProvider.get(); 
    } // ... 
    } 
} 
+0

感謝您提供深入的解釋和解決方案。 –

+0

實際實現註釋的一個很好的替代方法是使用'@Pro''方法。 –

+0

@Tavian:是!上面提到的(「很好地工作......」)。 –

1

can't do that

@BindingAnnotation告訴吉斯,這是一個綁定註釋。 如果多個綁定註釋適用於同一成員,Guice將產生錯誤。

您可以改爲使用命名綁定,或者您應該考慮重新設計解決方案。

+0

我可以添加多個名稱而不只是一個長名稱嗎?如果可能,我想避免使用像'states_tore.in_memory.number'這樣的名字... –