我的代码中有:
private static class BaseScriptInfoParser extends NodeParser<Asset.ScriptInfo> {
private Asset.ScriptKindEnum scriptKind;
private final NodeParser<Asset.TransformerKindEnum> transformerKindNodeParser;
private final NodeParser<Asset.ValidatorKindEnum> validatorKindNodeParser;
@Inject
BaseScriptInfoParser(
// First two arguments are injectors
@Named("transformerKind") NodeParser<Asset.TransformerKindEnum> transformerKindNodeParser,
@Named("validatorKind") NodeParser<Asset.ValidatorKindEnum> validatorKindNodeParser,
Asset.ScriptKindEnum scriptKind)
{
this.scriptKind = scriptKind;
this.transformerKindNodeParser = transformerKindNodeParser;
this.validatorKindNodeParser = validatorKindNodeParser;
}
// ...
}
现在我想创建一个没有代码的BaseScriptInfoParser
实例(因为我学到它应该只在Main函数中)
Injector injector = Guice.createInjector(new BoilerModule());
// ...
我可以只调用一个构造函数来创建一个带有一个参数(BaseScriptInfoParser
类型)的Asset.ScriptKindEnum
类的对象,并且自动注入前两个参数吗?
或者如何使用注射器创建物体?
如果构造函数BaseScriptInfoParser
没有第三个参数,它将如何工作?
你是正确的,以避免额外调用createInjector
,并避免绕过你创建的注入器。为了获得最佳Guice实践,您应该准确指定任何给定组件创建或依赖的对象,而Injector则与之相反:它可以创建任何内容。
相反,通常,您应该从图中注入所需的对象。如果您认为以后可能只需要一个对象,或者您可能需要一个对象的多个实例,则可以注入一个Provider<T>
(其中T是图中可用的任何对象),然后您可以稍后请求该实例,就好像您称为getInstance
(但不创建新对象或使图表的其余部分可用)。这应该使测试更容易,因为simulating a Provider in tests is very easy,但模拟注射器是困难的,并且使用真正的注射器是昂贵和复杂的。
如果BaseScriptInfoParser没有这个手动的第三个参数,你可以注入Provider<BaseScriptInfoParser>
:只要BaseScriptInfoParser
在模块中有一个公共无参数构造函数,一个@Inject
注释构造函数或一个bind(BaseScriptInfoParser.class)
绑定或@Provides BaseScriptInfoParser
方法,Guice就会自动处理它。
现在,关于混合注入的构造函数参数和非注入参数:
并非图中的每个对象都需要注入:要使用来自他的"To new
or not to new
" article的MiškoHevery的术语,您的应用程序很可能由来自图形的注入组成,有一些新的东西,如“值对象”和“数据对象”,有很多状态和没有依赖。
但是,对于某些对象,有了构造函数提供的不可变状态,同时还从图中访问注入,而不将两者分成单独的对象(这也是一个选项)是有意义的。实际上,您所希望的是DI框架可以提供的对象,它实现了以下接口:
interface BaseScriptInfoParserFactory {
/**
* Calls the BaseScriptInfoParser constructor, with other constructor params
* injected from the graph.
*/
BaseScriptInfoParser create(Asset.ScriptKindEnum scriptKind);
}
因为这是一个非常明确的类,所以Google提供了几种不同的选项来自动生成一个:你可以使用Guice的反射Assisted Injection或AutoFactory来自代码生成的Google Auto包。后者有点快,因为它生成普通代码而不是运行时反射代码,但前者与Guice集成得更好:
@Inject BaseScriptInfoParser(
@Named("transformerKind") NodeParser<...> transformerKindNodeParser,
@Named("validatorKind") NodeParser<...> validatorKindNodeParser,
@Assisted Asset.ScriptKindEnum scriptKind)
public class BaseScriptInfoParser {
public interface Factory {
// Any interface and method name works. These are the most common.
BaseScriptInfoParser create(Asset.ScriptKindEnum scriptKind);
}
// ... rest of the class, including the above constructor
}
public class YourModule extends AbstractModule {
@Override public void configure() {
install(new FactoryModuleBuilder()
.build(BaseScriptInfoParser.Factory.class));
}
}
BaseScriptInfoParser.Factory
并在需要新物体时调用create(someScriptKind)
。