在我的项目中,我在所有地方都使用了依赖注入,并且在两种情况下都使用了临时工厂。首先,当我想精确控制创建实例的时间时,我注入了一个工厂而不是实例:
// WidgetA must be created before WidgetB, because of the side-effects
// on the container.
WidgetAFactory.make(container);
WidgetBFactory.make(container);
另一种情况是构造函数将可注入值和运行时值混合使用时。而不是使用:
@Inject
WidgetC(
Container,
@WidgetCFont Font,
@WidgetCColor Color,
@Named("flag") String flag) {
...
}
我使用:
@Inject
WidgetCFactory(
@WidgetCFont Font font,
@WidgetCColor Color color,
@Named("flag") String flag) {
...
}
WidgetCFactory.make(Container container) {
return new WidgetC(container, font, color, flag);
}
但是我在使用工厂时受到两个限制:
在我的第一个例子中,我还需要WidgetA成为其他@Injected构造函数所需要的@Singleton。到目前为止,我的解决方案是存储我在调用工厂时创建的实例,并@Provids提供给其他人使用。有没有办法让这个单例的控制权回到guice,而不必自己维护该实例?
在我的第二个示例中,管理注入的依赖关系是一个烂摊子:WidgetCFactory必须使用一长串注入值来调用WidgetC构造函数,必须针对依赖关系中的每个更改对其进行更新,并且不带注释检查。有没有办法向Guice提供运行时参数,并让它处理其他依赖项?
在两种情况下,我都可以使用一个子注入器,该子注入器将获得运行时值,并让Guice为工厂:
public static class WidgetCFactory {
private final Injector injector;
@Inject
public WidgetCFactory(Injector injector) {
this.injector = injector;
}
public WidgetC make(Container container) {
Injector childInjector = injector.createChildInjector(new AbstractModule() {
@Override protected void configure() {
bind(Container.class).toInstance(container);
}
});
return childInjector.getInstance(WidgetC.class);
}
}
但是我发现很多人没有这样做。是因为它太重,还是超出了依赖注入的良好实践?有什么更好的方法?
答案 0 :(得分:4)
混合注入值和运行时值意味着您应该查看“辅助注入”,它使您可以声明某些特定注入值将在运行时由调用站点提供,并生成将仅公开那些作为参数的工厂。在https://github.com/google/guice/wiki/AssistedInject中,您将希望为每种类型的模块安装一个以这种方式处理的模块,例如
// this goes in your existing Module.configure()
install(new FactoryModuleBuilder()
// you can add more than one type here in this way
.implement(WidgetC.class, WidgetC.class)
.build(WidgetFactory.class));
//...
public interface WidgetFactory {
// you can add more than one method here
WidgetC createWidgetC(Container container);
}
@AssistedInject
WidgetC(
@Assisted Container,
@WidgetCFont Font,
@WidgetCColor Color,
@Named("flag") String flag) {
...
}
尤其要注意对WidgetC
构造函数的更改,即构造函数上的不同注释(因为实际上通过常规注入构造是不安全的)和Container
参数(由工厂提供,而不是IoC容器。
要使WidgetA
为单例,可以用@Singleton
装饰类型,也可以用configure()
方法将其绑定:
bind(WidgetA.class).in(Singleton.class);
按照书面规定,它将被懒惰地创建-仅在首次请求后才存在,但是每次请求时,它都是同一实例,不会从头开始创建。
答案 1 :(得分:2)
要回应Colin's correct answer,必须使用辅助注入,而Guice从2.0开始就提供了辅助注入(通过单独的依赖项/ JAR)。您可以在Guice wiki AssistedInject page上阅读有关Guice的实现的更多信息,但是除了Colin编写的内容之外,我没有其他示例。
您可以考虑的另一种方法是在AutoFactory中,该代码为您生成工厂实现。 (它是Google Auto的一部分,它是Java的代码生成器套件,用于创建注释实现,服务,不可变的值对象和工厂。)它是Dagger的事实上的标准,但适用于包括Guice在内的任何JSR-330框架。>
关于问题#1,我将与Colin背道而驰,说您正在寻找的内容本质上有些危险:如果在应用程序的生存期内存在@Singleton
个对象,但是WidgetA Factory需要一个容器,那么您的WidgetA可能在容器准备就绪之前就存在,或者在容器被销毁之后就存在。
如果您的WidgetA容器也是@Singleton
,那么您可以在没有工厂的情况下创建WidgetA,并且一切顺利:您可以跳过工厂,绑定容器,正常绑定WidgetA并注入{{1} }(无需额外配置即可使用)以延迟WidgetA的创建,直到准备就绪为止。
如果您的真正要求是在容器存在的同时就存在WidgetA,但要让WidgetA / B / C当时都使用相同的Container和WidgetA,则可以考虑将a child injector绑定到您的容器和小部件。这样,每个Container都会获得自己的WidgetA,在该容器中每次注入WidgetA都是一致的,并且在获得新Container时将处置WidgetA。当然,如果您的Container仅在Injector正常工作后才开始可用,并且在此之后保持稳定,则可以将该子注射器用作主注射器,然后让WidgetA工作。
如果您的WidgetA依赖于尚未启动的容器,请当心:这可能是“范围扩大的注入”,因为即使您的容器在WidgetA中仍将以Provider<WidgetA>
的形式存在否则将被垃圾收集。充其量这可能是内存泄漏,并且在您的应用程序中存在多个容器的情况下,可能会引起奇怪的错误。您可以像往常一样使用有状态模块,但是无论如何都要非常小心。