如何使用Guice注射器创建对象?

时间:2017-12-18 18:23:18

标签: java constructor guice

我的代码中有:

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没有第三个参数,它将如何工作?

1 个答案:

答案 0 :(得分:3)

你是正确的,以避免额外调用createInjector,并避免传递你创建的注入器。为了获得最佳Guice实践,您应该准确指定任何给定组件创建或依赖的对象,而Injector则与之相反:它可以创建任何内容。

相反,通常,您应该从图中注入所需的对象。如果您认为以后可能只需要一个对象,或者您可能需要多个对象实例,则可以注入Provider<T>(其中T是图中可用的任何对象),然后您可以请求实例之后好像你调用getInstance(但没有创建新对象或使图表的其余部分可用)。这也应该使测试更容易,因为simulating a Provider in tests is very easy,但是模拟注射器很困难,使用真正的注射器既昂贵又复杂。

如果BaseScriptInfoParser没有这个手动的第三个参数,你可以注入Provider<BaseScriptInfoParser>:只要BaseScriptInfoParser有一个公共无参数构造函数,@Inject注释,Guice会自动处理它构造函数,或模块中的bind(BaseScriptInfoParser.class)绑定或@Provides BaseScriptInfoParser方法。

现在,关于混合注入的构造函数参数和非注入参数:

并非图表中的每个对象都必须是可注入的:要使用Mi "To new or not to new" article中的MiškoHevery术语,您的应用程序很可能由来自图表的注入组成,有些 newables ,如“值对象”和“数据对象”,它们具有大量状态且没有依赖关系。

但是,对于某些对象,使用构造函数提供的不可变状态同时还从图形访问注入表是有意义的,而不将两者分成单独的对象(这也是一个选项)。实际上,您所希望的是DI框架可以提供的对象,它实现了这个界面:

interface BaseScriptInfoParserFactory {
  /**
   * Calls the BaseScriptInfoParser constructor, with other constructor params
   * injected from the graph.
   */
  BaseScriptInfoParser create(Asset.ScriptKindEnum scriptKind);
}

因为这是一个非常明确定义的类,所以Google提供了几种不同的选项来自动生成一个:您可以使用Guice的反射Assisted InjectionAutoFactory代码 - 生成Google Auto包。后者有点快,因为它生成普通代码而不是运行时反射代码,但前者与Guice集成得更好:

  1. 确保Guice Assisted Injection JAR位于类路径中。它是独立的。

  2. 标记你的构造函数,说出哪些参数应该来自Guice:

    @Inject BaseScriptInfoParser(
          @Named("transformerKind") NodeParser<...> transformerKindNodeParser,
          @Named("validatorKind") NodeParser<...> validatorKindNodeParser,
          @Assisted Asset.ScriptKindEnum scriptKind)
    
  3. 编写一个可以注入的Factory接口,我喜欢将其作为嵌套接口:

    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
    }
    
  4. 告诉Guice写一个实现并绑定它:

    public class YourModule extends AbstractModule {
      @Override public void configure() {
        install(new FactoryModuleBuilder()
            .build(BaseScriptInfoParser.Factory.class));
      }
    }
    
  5. 只要您需要新对象,就会注入BaseScriptInfoParser.Factory并致电create(someScriptKind)