如何使用 Guice 注入器创建对象?
How to create objects with Guice injectors?
我的代码中有:
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
class 的对象,带有一个参数(Asset.ScriptKindEnum
类型)并自动注入前两个参数吗?
或者如何使用注入器创建对象?
如果构造函数 BaseScriptInfoParser
没有第三个参数,它会如何工作?
避免额外调用 createInjector
并避免传递您创建的 Injector 是正确的。对于最佳 Guice 实践,您应该准确指定任何给定组件创建或依赖的对象,而 Injector 恰恰相反:它可以创建任何东西。
相反,一般来说,您应该从图中注入您需要的对象。如果你认为你以后可能只需要一个对象,或者你可能需要一个对象的多个实例,你可以注入一个 Provider<T>
代替(其中 T 是图中可用的任何对象),然后你可以请求实例稍后就好像您调用了 getInstance
(但没有创建新对象或使图形的其余部分可用)。这也应该使测试更容易,因为 simulating a Provider in tests is very easy,但是模拟 Injector 很困难,使用真实的 Injector 既昂贵又复杂。
如果 BaseScriptInfoParser 没有这个手动的第三个参数,你可以只注入 Provider<BaseScriptInfoParser>
:Guice 会自动处理这个只要 BaseScriptInfoParser
有一个 public 无参数构造函数,一个@Inject
带注释的构造函数,或模块中的 bind(BaseScriptInfoParser.class)
绑定或 @Provides BaseScriptInfoParser
方法。
现在,关于混合注入构造函数参数和非注入参数:
并非图表中的每个对象都需要是可注入的:要使用 Miško Hevery 的 "To new
or not to new
" article 术语,您的应用程序可能由 injectables 组成,它们来自该图,其中一些 newables 像 "value objects" 和 "data objects" 有很多状态并且没有依赖关系。
但是,对于某些对象,构造函数提供的不可变状态同时从图中访问可注入对象是有意义的,而不将两者分离为单独的对象(这也是一个选项)。实际上,您想要的是您的 DI 框架可以提供的对象,它实现了这个接口:
interface BaseScriptInfoParserFactory {
/**
* Calls the BaseScriptInfoParser constructor, with other constructor params
* injected from the graph.
*/
BaseScriptInfoParser create(Asset.ScriptKindEnum scriptKind);
}
因为这是一个非常明确的 class 编写,Google 提供了几个不同的选项来自动生成一个:您可以使用 Guice 的反射 Assisted Injection or AutoFactory from the code-generating Google Auto包裹。后者更快一些,因为它生成普通代码而不是运行时反射代码,但前者与 Guice 的集成稍微好一点:
确保 Guice 辅助注入 JAR 在 class 路径上。是分开的。
标记您的构造函数以说明哪些参数应来自 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
}
告诉 Guice 编写一个实现并绑定到它:
public class YourModule extends AbstractModule {
@Override public void configure() {
install(new FactoryModuleBuilder()
.build(BaseScriptInfoParser.Factory.class));
}
}
注入您的 BaseScriptInfoParser.Factory
并在需要新对象时调用 create(someScriptKind)
。
我的代码中有:
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
class 的对象,带有一个参数(Asset.ScriptKindEnum
类型)并自动注入前两个参数吗?
或者如何使用注入器创建对象?
如果构造函数 BaseScriptInfoParser
没有第三个参数,它会如何工作?
避免额外调用 createInjector
并避免传递您创建的 Injector 是正确的。对于最佳 Guice 实践,您应该准确指定任何给定组件创建或依赖的对象,而 Injector 恰恰相反:它可以创建任何东西。
相反,一般来说,您应该从图中注入您需要的对象。如果你认为你以后可能只需要一个对象,或者你可能需要一个对象的多个实例,你可以注入一个 Provider<T>
代替(其中 T 是图中可用的任何对象),然后你可以请求实例稍后就好像您调用了 getInstance
(但没有创建新对象或使图形的其余部分可用)。这也应该使测试更容易,因为 simulating a Provider in tests is very easy,但是模拟 Injector 很困难,使用真实的 Injector 既昂贵又复杂。
如果 BaseScriptInfoParser 没有这个手动的第三个参数,你可以只注入 Provider<BaseScriptInfoParser>
:Guice 会自动处理这个只要 BaseScriptInfoParser
有一个 public 无参数构造函数,一个@Inject
带注释的构造函数,或模块中的 bind(BaseScriptInfoParser.class)
绑定或 @Provides BaseScriptInfoParser
方法。
现在,关于混合注入构造函数参数和非注入参数:
并非图表中的每个对象都需要是可注入的:要使用 Miško Hevery 的 "To new
or not to new
" article 术语,您的应用程序可能由 injectables 组成,它们来自该图,其中一些 newables 像 "value objects" 和 "data objects" 有很多状态并且没有依赖关系。
但是,对于某些对象,构造函数提供的不可变状态同时从图中访问可注入对象是有意义的,而不将两者分离为单独的对象(这也是一个选项)。实际上,您想要的是您的 DI 框架可以提供的对象,它实现了这个接口:
interface BaseScriptInfoParserFactory {
/**
* Calls the BaseScriptInfoParser constructor, with other constructor params
* injected from the graph.
*/
BaseScriptInfoParser create(Asset.ScriptKindEnum scriptKind);
}
因为这是一个非常明确的 class 编写,Google 提供了几个不同的选项来自动生成一个:您可以使用 Guice 的反射 Assisted Injection or AutoFactory from the code-generating Google Auto包裹。后者更快一些,因为它生成普通代码而不是运行时反射代码,但前者与 Guice 的集成稍微好一点:
确保 Guice 辅助注入 JAR 在 class 路径上。是分开的。
标记您的构造函数以说明哪些参数应来自 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 }
告诉 Guice 编写一个实现并绑定到它:
public class YourModule extends AbstractModule { @Override public void configure() { install(new FactoryModuleBuilder() .build(BaseScriptInfoParser.Factory.class)); } }
注入您的
BaseScriptInfoParser.Factory
并在需要新对象时调用create(someScriptKind)
。