简体   繁体   English

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

[英]How to create objects with Guice injectors?

I have in my code: 我的代码中有:

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;
    }

    // ...

}

Now I want to create an instance of BaseScriptInfoParser without code like (because as I learned it should be only in Main function) 现在我想创建一个没有代码的BaseScriptInfoParser实例(因为据我BaseScriptInfoParser它应该只在Main函数中)

Injector injector = Guice.createInjector(new BoilerModule());
// ...

Can I just call a constructor to create an object of BaseScriptInfoParser class with one argument (of Asset.ScriptKindEnum type) and have the first two arguments to be injected automatically? 我可以只调用一个构造函数来创建一个BaseScriptInfoParser类的对象,该对象具有一个参数( Asset.ScriptKindEnum类型),并且前两个参数是自动注入的吗?

Or how does creation of objects with injectors work? 或者如何使用注射器创建物体?

And how would it work if the constructor BaseScriptInfoParser didn't have the third parameter? 如果构造函数BaseScriptInfoParser没有第三个参数,它将如何工作?

You are correct to avoid additional calls to createInjector , and to avoid passing around the Injector you create. 您是正确的,以避免额外调用createInjector ,并避免传递您创建的Injector。 For best Guice practices, you should be specifying exactly what objects any given component creates or depends on, and Injector is the opposite of that: it can create anything. 为了获得最佳Guice实践,您应该准确指定任何给定组件创建或依赖的对象,而Injector则与之相反:它可以创建任何内容。

Instead, in general, you should inject the objects that you need from the graph. 相反,通常,您应该从图中注入所需的对象。 If you think you might only need an object later, or you might need several instances of an object, you can inject a Provider<T> instead (where T is any object available in the graph), and then you can request the instance later as if you called getInstance (but without creating a new object or making the rest of the graph available). 如果您认为以后可能只需要一个对象,或者您可能需要多个对象实例,则可以注入Provider<T> (其中T是图中可用的任何对象),然后您可以稍后请求该实例好像你调用了getInstance (但没有创建新对象或者使图表的其余部分可用)。 This should make testing much easier as well, because simulating a Provider in tests is very easy , but simulating an Injector is difficult, and using a real Injector is expensive and complicated. 这应该使测试更容易,因为在测试中模拟提供者非常容易 ,但是模拟注入器很困难,并且使用真正的注入器是昂贵且复杂的。

If BaseScriptInfoParser didn't have this manual third parameter, you could just inject Provider<BaseScriptInfoParser> : Guice would automatically handle this as long as BaseScriptInfoParser had a public parameterless constructor, an @Inject annotated constructor, or a bind(BaseScriptInfoParser.class) binding or @Provides BaseScriptInfoParser method in the module. 如果BaseScriptInfoParser没有这个手动的第三个参数,你可以只注入Provider<BaseScriptInfoParser> :只要BaseScriptInfoParser有一个公共无参数构造函数,一个@Inject注释的构造函数或一个bind(BaseScriptInfoParser.class)绑定,Guice就会自动处理它或@Provides BaseScriptInfoParser模块中的@Provides BaseScriptInfoParser方法。


Now, about mixing injected constructor parameters and non-injected parameters: 现在,关于混合注入的构造函数参数和非注入参数:

Not every object in your graph needs to be Injectable: To use Miško Hevery's terminology from his "To new or not to new " article , your application will likely be composed of injectables that come from the graph, with some newables like "value objects" and "data objects" that have lots of state and no dependencies. 并非图中的每个对象都需要注入:要使用MiškoHevery的术语,从他的“To new or not to new ”文章中 ,您的应用程序很可能由来自图表的注射剂组成,并带有一些新的东西,如“值对象”和“数据对象”具有大量的状态和没有依赖关系。

However, for certain objects, it makes sense to have constructor-provided immutable state while also accessing injectables from the graph , without separating the two into separate objects (which is also an option). 但是,对于某些对象,有了构造函数提供的不可变状态,同时还从图中访问注入 ,而不将两者分成单独的对象(这也是一个选项)是有意义的。 Effectively, what you'd like is an object that your DI framework can provide, that fulfills this interface: 实际上,您所希望的是DI框架可以提供的对象,它实现了以下接口:

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

Because this is a very well-defined class to write, Google offers a couple of different options for how to generate one automatically: you can use either Guice's reflective Assisted Injection or AutoFactory from the code-generating Google Auto package. 因为这是一个非常明确的类,所以Google提供了几种不同的选项来自动生成一个:您可以使用Guice的反射辅助注入或来自代码生成Google Auto软件包的AutoFactory The latter is a bit faster because it generates normal code rather than runtime reflective code, but the former integrates slightly better with Guice: 后者有点快,因为它生成普通代码而不是运行时反射代码,但前者与Guice集成得更好:

  1. Make sure the Guice Assisted Injection JAR is on the classpath. 确保Guice Assisted Injection JAR在类路径上。 It's separate. 它是分开的。

  2. Mark your constructor to say which parameters should come from Guice: 标记你的构造函数,说出哪些参数应该来自Guice:

     @Inject BaseScriptInfoParser( @Named("transformerKind") NodeParser<...> transformerKindNodeParser, @Named("validatorKind") NodeParser<...> validatorKindNodeParser, @Assisted Asset.ScriptKindEnum scriptKind) 
  3. Write a Factory interface that you can inject, which I like to do as a nested interface: 编写一个可以注入的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. Tell Guice to write an implementation and bind to it: 告诉Guice写一个实现并绑定到它:

     public class YourModule extends AbstractModule { @Override public void configure() { install(new FactoryModuleBuilder() .build(BaseScriptInfoParser.Factory.class)); } } 
  5. Inject your BaseScriptInfoParser.Factory and call create(someScriptKind) whenever you need a new object. 注入BaseScriptInfoParser.Factory并在需要新对象时调用create(someScriptKind)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM