2017-05-26 104 views
1

我與一個特定的依賴注入問題所困擾,我只是似乎無法弄清楚。僅供參考:我對guice很陌生,但我有其他DI框架的經驗 - 這就是爲什麼我認爲這不應該很難實現。吉斯多個實現參數的構造函數與依賴

我在做什麼: 我正在Lagom多模塊項目,並使用吉斯作爲DI。

我想實現的目標: 注入一些接口實現的多個命名實例(讓我們稱之爲發佈者,因爲它將發佈消息給kafka主題)傳遞給我的服務。 這個'發佈者'注入了一些Lagom和Akka相關的服務(ServiceLocator,ActorSystem,Materializer等)。

現在,我想有這樣的出版商的兩個實例,並且每個將消息發佈到不同的主題(每個主題一個如此出版商實例)。

我該怎麼做到這一點? 我有一個或多個實例的同一主題沒有問題,但如果我想爲每個實例注入不同的主題名稱我有一個問題。

所以,我的出版商實施的構造看起來像這樣:

@Inject 
public PublisherImpl(
    @Named("topicName") String topic, 
    ServiceLocator serviceLocator, 
    ActorSystem actorSystem, 
    Materializer materializer, 
    ApplicationLifecycle applicationLifecycle) { 
... 
} 

如果我想創建一個實例我會做這樣的在我ServiceModule:

public class FeedListenerServiceModule extends AbstractModule implements ServiceGuiceSupport { 
    @Override 
    protected void configure() { 
     bindService(MyService.class, MyServiceImpl.class); 
     bindConstant().annotatedWith(Names.named("topicName")).to("topicOne"); 
     bind(Publisher.class).annotatedWith(Names.named("publisherOne")).to(PublisherImpl.class); 
    } 
} 

我將如何綁定多個出版商每個人都有自己的話題?

我玩弄實現另一個私有模塊:

public class PublisherModule extends PrivateModule { 

    private String publisherName; 
    private String topicName; 

    public PublisherModule(String publisherName, String topicName) { 
     this.publisherName = publisherName; 
     this.topicName = topicName; 
    } 

    @Override 
    protected void configure() { 
     bindConstant().annotatedWith(Names.named("topicName")).to(topicName); 
     bind(Publisher.class).annotatedWith(Names.named(publisherName)).to(PublisherImpl.class); 
    } 
} 

但這使我無處因爲你不能在你得到噴射器模塊的配置方法:

Injector injector = Guice.createInjector(this); // This will throw IllegalStateException : Re-entry is not allowed 
injector.createChildInjector(
    new PublisherModule("publisherOne", "topicOne"), 
    new PublisherModule("publisherTwo", "topicTwo")); 

唯一的解決方案是容易的,它的工作原理是,我改變我的PublisherImpl到抽象,加上他的抽象的「getTopic()」方法,並與主題覆蓋增加兩個實現。

但是這種解決方案是瘸子。爲代碼重用添加額外的繼承並不是最佳實踐。此外,我認爲,吉斯肯定必須支持這樣的功能。

的任何建議都歡迎。 KR,內伊奇

回答

1

Guice的方式依賴注入的是,DI框架補充你的實例化邏輯,它不會取代它。凡能,它將實例事情對你,但它並沒有試圖太聰明瞭。它也不會將配置(主題名稱)與依賴注入相混淆 - 它只執行一項操作,即DI,並且做得很好。所以你不能用它來配置東西,比如你用Spring的方式。所以如果你想用兩個不同的參數實例化一個對象,那麼你用兩個不同的參數來實例化這個對象 - 也就是說,你調用new兩次。這可以通過使用供應商的方法,這是記錄在這裏進行:

https://github.com/google/guice/wiki/ProvidesMethods

在你的情況下,它可能看起來像添加下面的方法到你的模塊:

@Provides 
@Named("publisherOne") 
@Singleton 
Publisher providePublisherOne(ServiceLocator serviceLocator, 
    ActorSystem actorSystem, 
    Materializer materializer, 
    ApplicationLifecycle applicationLifecycle) { 
    return new PublisherImpl("topicOne", serviceLocator, 
     actorSystem, materializer, applicationLifecycle); 
} 

而且,你如果你添加一個生命週期鉤子,可能會希望它是一個singleton,否則每次你在每次實例化的時候添加一個新鉤子都會遇到內存泄漏。

3

不要在配置方法中創建新的Injector。相反,install您創建的新模塊。不需要兒童注射器 - 如在PrivateModule文檔中,「私人模塊使用父注射器實施」,因此無論如何都有兒童注射器。

install(new PublisherModule("publisherOne", "topicOne")); 
install(new PublisherModule("publisherTwo", "topicTwo")); 

你使用PrivateModule的技術是一個我想一起去在這種情況下,特別是考慮到使綁定可通過爲你擁有了它綁定註釋的願望,特別是如果整套主題是在運行時已知。您甚至可以將呼叫轉至install進行循環。

但是,如果您需要任意數量的實現,您可能需要創建一個可注入的工廠或提供程序,您可以在運行時向其傳遞字符串。

public class PublisherProvider { 
    // You can inject Provider<T> for all T bindings in Guice, automatically, which 
    // lets you configure in your Module whether or not instances are shared. 
    @Inject private final Provider<ServiceLocator> serviceLocatorProvider; 
    // ... 

    private final Map<String, Publisher> publisherMap = new HashMap<>(); 

    public Publisher publisherFor(String topicName) { 
    if (publisherMap.containsKey(topicName)) { 
     return publisherMap.get(topicName); 
    } else { 
     PublisherImpl publisherImpl = new PublisherImpl(
      topicName, serviceLocatorProvider.get(), actorSystemProvider.get(), 
      materializerProvider.get(), applicationLifecycleProvider.get()); 
     publisherMap.put(topicName, publisherImpl); 
     return publisherImpl; 
    } 
    } 
} 

你可能會想作出上述線程安全的;另外,可以使用assisted injectionFactoryModuleBuilder)或AutoFactory避免顯式的構造函數調用,它將自動通過顯式參數(如topicName),同時注入像ServiceLocator之類的DI提供程序(希望具有特定目的,因爲您可能不需要太多的服務 - 無論如何,在DI框架內定位!)。

(旁註:不要忘了expose您的註解爲您PrivateModule綁定如果沒有發現自己注射的topicName其他地方,你也可以考慮用個人@Provides方法與輔助注射或以上AutoFactory方法。 ,但如果您希望每個發佈者都需要不同的對象圖,則可以選擇PrivateModule方法。)