Guice 多重实现,带依赖的参数化构造函数
Guice multiple implementations, parameterized constructor with dependencies
我正在努力解决一个特定的依赖项注入问题,但我似乎无法弄清楚。仅供参考:我是 guice 的新手,但我有使用其他 DI 框架的经验 - 这就是为什么我认为这不应该太复杂来实现。
我在做什么:
我正在研究 Lagom 多模块项目并使用 Guice 作为 DI。
我想达到的目标:
将某些接口实现的多个命名实例(我们称之为发布者,因为它将向 kafka 主题发布消息)注入我的服务。
'publisher' 注入了一些 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()' 方法并添加另外两个具有主题覆盖的实现。
但是这个解决方案很蹩脚。为代码重用添加额外的继承并不是最佳实践。另外我相信Guice肯定支持这样的功能。
欢迎任何建议。
KR, Nejc
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);
}
此外,如果要添加生命周期挂钩,您可能希望它是单例,否则每次实例化时添加新挂钩时都可能 运行 导致内存泄漏。
不要在配置方法中创建新的注入器。相反,install
the new modules you create. No child injectors needed—as in the PrivateModule 文档,"Private modules are implemented using parent injectors",所以无论如何都涉及到一个子注射器。
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 injection (FactoryModuleBuilder) or AutoFactory 来避免显式构造函数调用,它会自动传递显式参数,例如 topicName,同时注入 DI 提供程序,例如 ServiceLocator(希望它有一个特定的目的,因为您可能不需要太多的服务定位无论如何都在 DI 框架内!)。
(旁注:不要忘记 expose
你的 PrivateModule 注释绑定。如果你没有发现自己在其他任何地方注入你的 topicName
,你也可以考虑使用个人 @Provides
方法与上面的辅助注入或 AutoFactory 方法,但如果您希望每个 Publisher 需要一个不同的对象图,您可以选择 PrivateModule 方法。)
我正在努力解决一个特定的依赖项注入问题,但我似乎无法弄清楚。仅供参考:我是 guice 的新手,但我有使用其他 DI 框架的经验 - 这就是为什么我认为这不应该太复杂来实现。
我在做什么: 我正在研究 Lagom 多模块项目并使用 Guice 作为 DI。
我想达到的目标: 将某些接口实现的多个命名实例(我们称之为发布者,因为它将向 kafka 主题发布消息)注入我的服务。 'publisher' 注入了一些 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()' 方法并添加另外两个具有主题覆盖的实现。
但是这个解决方案很蹩脚。为代码重用添加额外的继承并不是最佳实践。另外我相信Guice肯定支持这样的功能。
欢迎任何建议。 KR, Nejc
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);
}
此外,如果要添加生命周期挂钩,您可能希望它是单例,否则每次实例化时添加新挂钩时都可能 运行 导致内存泄漏。
不要在配置方法中创建新的注入器。相反,install
the new modules you create. No child injectors needed—as in the PrivateModule 文档,"Private modules are implemented using parent injectors",所以无论如何都涉及到一个子注射器。
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 injection (FactoryModuleBuilder) or AutoFactory 来避免显式构造函数调用,它会自动传递显式参数,例如 topicName,同时注入 DI 提供程序,例如 ServiceLocator(希望它有一个特定的目的,因为您可能不需要太多的服务定位无论如何都在 DI 框架内!)。
(旁注:不要忘记 expose
你的 PrivateModule 注释绑定。如果你没有发现自己在其他任何地方注入你的 topicName
,你也可以考虑使用个人 @Provides
方法与上面的辅助注入或 AutoFactory 方法,但如果您希望每个 Publisher 需要一个不同的对象图,您可以选择 PrivateModule 方法。)