在我的项目中,我在任何地方使用依赖注入,并且我在两个案例中使用临时工厂。首先,当我想要精确控制创建实例的时间时,我会注入工厂而不是实例:
// 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);
}
但是我使用工厂受到两个限制:
感觉就像这两种情况一样,我可以使用一个可以获得运行时值的子注入器,让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);
}
}
但我没有发现很多人这样做的情况。是因为它太重了,还是在依赖注入的良好做法之外?什么是更好的方式?
混合注入值和运行时值意味着您应该查看“辅助注入”,它允许您声明在运行时由调用点提供的某些注入值,以及生成的工厂,它将仅公开那些作为参数。从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);
如上所述,它将被懒惰地创建 - 它将仅在首次请求之后存在,但是每次请求它时,它将是相同的实例并且不会从头开始创建。
为了回应Colin's correct answer,辅助注射是最佳选择,Guice自2.0以来就提供了辅助注射(通过单独的依赖/ JAR)。你可以在Guice wiki AssistedInject page上阅读更多关于Guice的实现,但除了科林所写的内容之外我不会有任何实例。
您可以考虑的另一种选择是AutoFactory,它为您编写代码生成工厂实现。 (它是Google Auto的一部分,是一套用于Java的代码生成器,用于创建注释实现,服务,不可变值对象和工厂。)它是Dagger的事实标准,但适用于包括Guice在内的任何JSR-330框架。
关于你的问题#1,我将与科林分道扬声,说你正在寻找的东西本来就有些危险:如果@Singleton
对象在你的应用程序的生命周期中存在,但你的WidgetA工厂需要一个容器,那么它可能是您的WidgetA在Container准备好之前存在,或者在Container被销毁之后存在。
如果您的WidgetA容器也是@Singleton
,那么您可以创建没有Factory的WidgetA,一切顺利:您可以跳过Factory,绑定Container,正常绑定WidgetA,并注入Provider<WidgetA>
(可用,无需额外配置)以延迟创建WidgetA直到你准备好了。
如果您的实际请求是WidgetA确实存在,只要Container存在,但是对于WidgetA / B / C,所有人都使用相同的Container和WidgetA,您可以考虑a child injector绑定Container和Widgets的位置。这样每个Container都有自己的WidgetA,WidgetA的每次注入在该容器中都是一致的,当你得到一个新的Container时,你将处理WidgetA。当然,如果您的容器仅在您的Injector工作后才开始可用并且在此之后保持一致,您可以使用该子注射器作为主注射器并在此之后使用WidgetA。
如果你的WidgetA依赖于一个没有开始的容器,那么要小心:这可能是一个“范围扩大注入”,因为你的容器将在WidgetA中以@Singleton
的形式存在,即使它会被垃圾收集。这可能是内存泄漏,并且可能导致应用程序中存在多个容器的奇怪错误。您可以使用有状态的模块,但在任何情况下都要非常小心。