Guice @Provides 方法与提供者 类

Guice @Provides Methods vs Provider Classes

我正在处理一个相当大的项目,其中有很多注入。我们目前正在使用 class 实现 Provider 每次需要一次的注入,它们大多只有一行 get 方法。

每次我需要一个新的供应商时,都要创建一个新的 class 开始变得烦人。在我的 Module 中使用提供者 classes 而不是 @Provides 方法有什么好处,反之亦然?

据我所知,对于大多数简单的情况,它们是完全等价的。

/**
 * Class-style provider.
 * In module: bind(Foo.class).annotatedWith(Quux.class).toProvider(MyProvider.class);
 */
class MyProvider implements Provider<Foo> {
  @Inject Dep dep;  // All sorts of injection work, including constructor injection.

  @Override public Foo get() {
    return dep.provisionFoo("bar", "baz");
  }
}

/**
 * Method-style provider. configure() can be empty, but doesn't have to be.
 */
class MyModule extends AbstractModule {
  /** Name doesn't matter. Dep is injected automatically. */
  @Provides @Quux public Foo createFoo(Dep dep) {
    return dep.provisionFoo("bar", "baz");
  }

  @Override public void configure() { /* nothing needed in here */ }
}

在任何一种风格中,Guice 都允许您注入 FooProvider<Foo>,即使密钥绑定到 class 或实例。如果直接获取实例,Guice 会自动调用 get,如果实例不存在,则会创建一个隐式的 Provider<Foo>。绑定注释适用于两种样式。

@Provides 的主要优点是紧凑,尤其是与匿名内部 Provider 实现相比。但是请注意,在某些情况下您可能希望支持 Provider classes:

  • 您可以创建自己的长寿命 Provider 实例,可能带有构造函数参数,并将键绑定到这些实例而不是 class 文字。

    bind(Foo.class).toProvider(new FooProvisioner("bar", "baz"));
    
  • 如果您使用的是与 JSR 330 (javax.inject) 兼容的框架,您可以轻松绑定到 javax.inject.Provider classes 或实例。 com.google.inject.Provider 扩展了该接口。

    bind(Foo.class).toProvider(SomeProviderThatDoesntKnowAboutGuice.class);
    
  • 您的提供程序可能很复杂,可以考虑到它自己的 class。根据您构建测试的方式,以这种方式测试您的 Provider 可能更容易。

  • 提供商可以扩展抽象 classes。使用@Provides 方法执行此操作可能并不容易或不直观。

  • 您可以将多个key直接绑定到同一个Provider上。每个 @Provides 方法只生成一个绑定,尽管您可以将其他键绑定到 key(此处为 @Quux Foo)并让 Guice 进行第二次查找。

  • 如果您想(例如)在不使用 Guice 作用域或绑定的情况下缓存或记忆实例,提供程序很容易装饰或包装。

    bind(Foo.class).toProvider(new Cache(new FooProvisioner("bar", "baz")));
    

重要:尽管对于 Guice 无法创建的 classes 来说,这是一个很好的策略,但请记住,Guice 可以自动创建并注入一个 Provider<T> 用于您以任何方式 bind 的任何 T,包括 class 名称、密钥或实例。除非涉及您自己的实际逻辑,否则无需创建显式提供程序。

类 的实例化方式也有所不同。 例如:

public class GumProvider implements Provider<Gum> {

  public Gum get() {
    return new Gum();
  }
}

public class GumModule extends AbstractModule {

    protected void configure() {

        bind(Gum.class).toProvider(GumProvider.class);
        //bind(Gum.class).to(GumballMachine.class);
    }
}

public class GumballMachine {

    @Inject
    private Provider<Gum> gumProvider;

    Gum gum;


    public Gum dispense() {
        return gumProvider.get();
    }
}

public class App {

    public static void main(String[] args) {
      // TODO Auto-generated method stub
      Injector injector = Guice.createInjector(new GumModule());
      GumballMachine m = injector.getInstance(GumballMachine.class);
      System.out.println(m.dispense());
      System.out.println(m.dispense());


    }

}

这将在每次调用时创建一个 Gum 实例。 而如果使用@Provides,相同的 Gum 实例将被传递给两个 Injectors