(その12) Providerについて

Spring統合の2回目の予定でしたが、前提としてProviderを理解していると良さそうだったので(というか、理解していないとAPIがよく分からなかったので)、予定を変更してProviderを見てみます。
Providerインタフェースは、Keyクラスと共にGuiceのDIの核となっているもので、一言で言うと「Guiceがinjectするインスタンスを提供するインタフェース」です。User's Guideに掲載されている図を引用します。
正確には原典を当たって頂くとして、Bindingはinjectする為の1組のマッピングで、Keyが「どこに(何に対して)injectするか」を示す値。Providerが、「injectするインスタンスを提供するインタフェース」です。つまり、色々なものをいっぱい省いて誤解を恐れつつ一言で言うと、「GuiceのDIとは、@Implementアノテーションがついた型に一致するKeyを探し、Providerから取得したインスタンスをぶちこむ処理」となりそうです。…いや、大雑把過ぎるかも。

単純なProviderの例

ともあれ、まずはカウントした数値のインスタンスを返す単純なProviderで試してみます。

  static class SimpleProvider implements Provider<Integer> {
    static int count = 0;
    public Integer get() {
      return Integer.valueOf(count++);
    }
  }

これを以下のコードでinjectしてみます。

  Injector injector = Guice.createInjector(new AbstractModule() {
    protected void configure() {
      // 通常このような普遍的なクラスを名前無しでbindしないと思いますが、例なので
      bind(Integer.class).toProvider(SimpleProvider.class);
    }
  });
  for(int i = 0; i < 5; i++) {
    System.out.println(injector.getInstance(Injectee.class).value);
  }
  //...
  static class Injectee {
    @Inject Integer value;
  }

結果は

0 1 2 3 4

予想通りです。この例から分かるのは、

  • injector.getInstance()するたびにget()が呼び出される(正確に言うと、シングルトンの場合は最初に1回だけ呼び出される)。
  • 前回までの例ではbind(インタフェース).to(実装クラス)という指定の仕方だったが、bind(インタフェース).toProvier(プロバイダ)という指定もできる。この場合、bindした型(インタフェース)に対してProvider.get()の戻り値がinjectされる。

という事です。

User's Guideの例

ただこれでは単なるFactoryですので、GuiceにおけるProviderの重要性がよく分かりません。なのでもう1つ、User's Guide(翻訳)の例を見てみましょう。

class WidgetProvider implements Provider<Widget> {
  final Service service;

  @Inject
  WidgetProvider(Service service) {
    this.service = service;
  }

  public Widget get() {
    return new Widget(service);
  }
}

これを見ると、Providerの使いどころの1つがハッキリ分かってきます。

  • 勝手にinjectできないクラス(この例だとServiceを直接@InjectできないWidgetクラス)のプロキシとなり、
  • そのクラスの依存性を解決した上でGuiceから使えるようにする

という役割です。また重要な事として、Provider自身にもinjectされます(上記の例ではServiceがinjectされる)。
Widgetが何にも依存しない単独のクラスであればbind(Widget.class)とかで済みますが、Serviceに依存しているこの例のような場合は、Providerを作る事によって依存性の全てをGuiceの管理下に置けるという訳ですね。
あとはUser'sGuideにある通り、bind(Widget.class).toProvider(WidgetProvider.class)で、@Inject Widget widget;にWidgetProvider.get()したインスタンスがinjectされるようになります。
以上でProviderの役割が多少分かったところで、次回はSpringIntegration#fromSpringメソッドを見てみたいと思います。