web-dev-qa-db-ja.com

Guice @ Providesメソッドとプロバイダークラス

私はたくさんの注射があるかなり大きなプロジェクトに取り組んでいます。現在、1つ必要なインジェクションごとにProviderを実装するクラスを使用しており、ほとんどの場合、1行のgetメソッドがあります。

新しいプロバイダーが必要になるたびに、新しいクラスを作成するのが面倒になり始めています。 Module@Providesメソッドを介してプロバイダークラスを使用するメリットはありますか、またはその逆はありますか?

9
Eliezer

私の知る限り、これらはほとんどの単純なケースとまったく同じです。

/**
 * 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ではFooおよびProvider<Foo>を挿入できます。 Guiceは、インスタンスを直接取得する場合は自動的にgetを呼び出し、インスタンスが存在しない場合は暗黙のProvider<Foo>を作成します。バインディングアノテーションは両方のスタイルで機能します。

@Providesの主な利点は、特に匿名の内部プロバイダー実装と比較した場合のコンパクトさです。ただし、プロバイダークラスを優先したい場合があることに注意してください。

  • おそらくコンストラクターパラメーターを使用して、独自の長寿命のプロバイダーインスタンスを作成し、クラスリテラルではなくそれらのインスタンスにキーをバインドできます。

    bind(Foo.class).toProvider(new FooProvisioner("bar", "baz"));
    
  • JSR 330(javax.inject)と互換性のあるフレームワークを使用している場合は、javax.inject.Providerクラスまたはインスタンスに簡単にバインドできます。 com.google.inject.Providerは、そのインターフェースを拡張します。

    bind(Foo.class).toProvider(SomeProviderThatDoesntKnowAboutGuice.class);
    
  • プロバイダーは、独自のクラスを考慮に入れるほど複雑な場合があります。テストの構成方法によっては、この方法でプロバイダーをテストする方が簡単な場合があります。

  • プロバイダーは抽象クラスを拡張できます。 @Providesメソッドを使用してこれを行うのは簡単でも直感的でもない場合があります。

  • 複数のキーを同じプロバイダーに直接バインドできます。各@Providesメソッドは1つのバインディングを生成しますが、他のキーをkey(ここでは@Quux Foo)にバインドして、Guiceに2番目の検索を行わせることができます。

  • (たとえば)Guiceのスコープやバインディングを使用せずにインスタンスをキャッシュまたはメモ化したい場合、プロバイダーは簡単に装飾またはラップできます。

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

[〜#〜]重要[〜#〜]:これは、Guiceが作成できないクラスにとっては良い戦略ですが、Guiceは自動的にProvider<T>を作成して注入できることに注意してくださいクラス名、キー、インスタンスなど、何らかの方法でbindしたTの場合。独自の実際のロジックが関係していない限り、明示的なプロバイダーを作成する必要はありません。

19
Jeff Bowman

クラスがインスタンス化される方法にも違いがあります。例:

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());


    }

}

これにより、呼び出しごとにガムのインスタンスが作成されます。一方、@ Providesが使用された場合、Gumの同じインスタンスが両方のインジェクターに渡されます。

0
Jamie J