2017-05-26 63 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方法。)