我正在努力解决特定的依赖注入问题,而我似乎无法弄明白 . 仅供参考:我是新手,但我有其他DI框架的经验 - 这就是为什么我认为这不应该是复杂的实现 .
我在做什么:我正在研究Lagom多模块项目并使用Guice作为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更改为abstract,添加抽象'getTopic()'方法并添加两个主题覆盖实现 .
但这个解决方案很蹩脚 . 为代码重用添加额外的继承并不是最佳实践 . 另外我相信Guice肯定必须支持这样的功能 .
欢迎任何建议 . KR,Nejc
2 回答
Guice的依赖注入方法是DI框架补充你的实例化逻辑,它不会取代它 . 它可以在哪里,它会为你实例化,但它不会试图过于聪明 . 它也不会将配置(主题名称)与依赖注入混淆 - 它做了一件事,DI,并做了一件好事 . 所以你不能用它来配置东西,例如Spring的方式 .
因此,如果要实例化具有两个不同参数的对象,则使用两个不同的参数实例化该对象 - 即,您两次调用
new
. 这可以通过使用提供程序方法来完成,这些方法在此处记录:https://github.com/google/guice/wiki/ProvidesMethods
在您的情况下,它可能看起来像是在您的模块中添加以下方法:
此外,如果您要添加生命周期钩子,您可能希望它是单例,否则每次在实例化时添加新钩子时都会遇到内存泄漏 .
不要在configure方法中创建新的Injector . 相反,install您创建的新模块 . 不需要儿童注射器 - 如PrivateModule文档,"Private modules are implemented using parent injectors",所以无论如何都要涉及儿童注射器 .
您使用PrivateModule的技术是我在这种情况下使用的技术,特别是考虑到希望通过绑定注释使绑定可用,特别是如果在运行时已知完整的主题集 . 您甚至可以将循环调用
install
.但是,如果需要任意数量的实现,则可能需要创建可在运行时传递String集的可注入工厂或提供程序 .
您可能想要使上述线程安全;此外,您可以通过使用assisted injection(FactoryModuleBuilder)或AutoFactory来避免显式构造函数调用,这将自动传递显式参数(如topicName),同时注入像ServiceLocator这样的DI提供程序(希望具有特定目的,因为您可能不需要太多服务 - 无论如何都要在DI框架内定位!) .
(旁注:不要忘记expose您的私人模块的注释绑定 . 如果您没有发现自己在其他任何地方注入
topicName
,您可能还会考虑使用上述辅助注射或AutoFactory方法的单独@Provides
方法,但如果您期望每个Publisher需要一个不同的对象图,你可以选择PrivateModule方法 . )