Dependency injection Guice多个实现,带依赖项的参数化构造函数

Dependency injection Guice多个实现,带依赖项的参数化构造函数,dependency-injection,guice,lagom,Dependency Injection,Guice,Lagom,我正在努力解决一个特殊的依赖注入问题,但我似乎无法解决它。仅供参考:我是guice的新手,但我有其他DI框架的经验——这就是为什么我认为这不应该太复杂而难以实现的原因 我在做什么: 我正在从事Lagom多模块项目,并使用Guice作为DI 我希望达到的目标是: 将某些接口实现的多个命名实例(我们称之为publisher,因为它会将消息发布到kafka主题)注入到我的服务中。 这个“发布者”已经注入了一些Lagom和Akka相关的服务(ServiceLocator、ActorSystem、Mate

我正在努力解决一个特殊的依赖注入问题,但我似乎无法解决它。仅供参考:我是guice的新手,但我有其他DI框架的经验——这就是为什么我认为这不应该太复杂而难以实现的原因

我在做什么: 我正在从事Lagom多模块项目,并使用Guice作为DI

我希望达到的目标是: 将某些接口实现的多个命名实例(我们称之为publisher,因为它会将消息发布到kafka主题)注入到我的服务中。 这个“发布者”已经注入了一些Lagom和Akka相关的服务(ServiceLocator、ActorSystem、Materializer等)

现在,我希望有两个这样的发布者实例,每个实例将消息发布到不同的主题(因此每个主题有一个发布者实例)

我将如何实现这一点? 我对同一主题的一个或多个实例没有问题,但是如果我想为每个实例注入不同的主题名称,我就有问题了

因此,我的publisher实现构造函数如下所示:

@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改为abstract,添加抽象的“getTopic()”方法,并添加另外两个具有主题覆盖的实现

但这一解决方案是站不住脚的。为代码重用添加额外的继承并不完全是最佳实践。而且我相信Guice肯定会支持这种特性

欢迎任何建议。
KR,Nejc

Guice的依赖注入方法是DI框架补充了您的实例化逻辑,而不是取代它。在可能的情况下,它会为您实例化一些东西,但它不会试图在这方面做得太聪明。它也不会混淆配置(主题名称)和依赖项注入——它只做了一件事,DI,而且做得很好。所以你不能用它来配置东西,比如用Spring

因此,如果你想用两个不同的参数实例化一个对象,那么你就用两个不同的参数实例化这个对象——也就是说,你调用
new
两次。这可以通过使用提供程序方法来实现,这些方法记录在此处:

在您的情况下,可能类似于将以下方法添加到模块中:

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

此外,如果您正在添加生命周期挂钩,您可能希望它是一个单例,否则每次您在实例化一个新挂钩时都会遇到内存泄漏。

Guice的依赖项注入方法是DI框架补充您的实例化逻辑,而不是取代它。在可能的情况下,它会为您实例化一些东西,但它不会试图在这方面做得太聪明。它也不会混淆配置(主题名称)和依赖项注入——它只做了一件事,DI,而且做得很好。所以你不能用它来配置东西,比如用Spring

因此,如果你想用两个不同的参数实例化一个对象,那么你就用两个不同的参数实例化这个对象——也就是说,你调用
new
两次。这可以通过使用提供程序方法来实现,这些方法记录在此处:

在您的情况下,可能类似于将以下方法添加到模块中:

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

另外,如果您正在添加生命周期挂钩,您可能希望它是一个单例,否则每次您在实例化新挂钩时都可能会遇到内存泄漏。

不要在配置方法中创建新的注入器。相反,您创建的新模块。无需子注入器,如文档中所述,“专用模块使用父注入器实现”,因此无论如何都会涉及子注入器

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;
    }
  }
}
公共类发布者提供者{
//您可以在Guice中自动为所有T绑定注入提供程序,这
//允许您在模块中配置实例是否共享。
@注入私有最终提供者ServiceLocator提供者;
// ...
private final Map publisherMap=new HashMap();
公共发布者发布者(字符串topicName){
if(publisherMap.containsKey(主题名称)){
返回publisherMap.get(topicName);
}否则{
PublisherImpl PublisherImpl=新的PublisherImpl(
topicName、serviceLocatorProvider.get()、actorSystemProvider.get(),
MaterialierProvider.get(),applicationLifecycleProvider.get();
publisherMap.put(主题名,publisherImpl);
返回RIMPL;
}
}
}
您可能希望使上述线程安全;此外,您可以通过使用()或()避免显式构造函数调用,这将在注入像ServiceLocator这样的DI提供者时自动传递像topicName这样的显式参数(希望这有一个特定的目的,因为您可能不需要在DI框架中进行太多的服务定位!)

(边注:不要忘记为您的私有模块注释您的绑定。如果您没有发现自己在任何地方注入您的<代码> TopICNEX/CODE,您也可以考虑使用辅助代码注入或自动填充器使用个人<代码> @提供< /COD>方法。