Google Guiceについて、もうちょっと調べた

[前回の記事](http://qiita.com/saka1_p/items/45fdf1f736173fcf1c5a)で紹介したのは、Google GuiceのDI機能のうちの一つ、Linked Bindingsだけでした。この記事はその続きです。

サンプルはScalaで書きましたが、例によってScalaっぽいコードが出てくるわけではないですね……Guiceをどう使えばScalaとよりよく連携できるようになるかは、まだよく分かっていません[^aisyo]。

[^aisyo]: もっとも、少なくともJavaとScalaの相性が良いのと同程度には、GuiceとScalaの相性も良いです。

さて、Guiceはもうちょっと色々な方法でDIができるので、それについても調べて紹介します。
以下では例示のために、前回も使用した`Service`とその実装クラス2つを使います。

```scala
trait Service {
  def doSomething(): Unit
}

class FakeService extends Service {
  def doSomething() { println("FakeService") }
}

class RealService extends Service {
  def doSomething() { println("RealService") }
}
```

## 用語
あるインタフェースに、特定の実装を結びつけることを**バインディング**(binding)と呼ぶようです。
バインディングを柔軟に制御することがDIフレームワークの目標です。

## さまざまなバインディング方式

### Binding Annotations
Linked Bindingでは、一つの型に一つの具体的な実装を配線させることができました。
Binding Annotationは、バインディングの配線条件をより細かくすることができる機能です。条件には`@Named`アノテーションを利用します。例えば「型Aは実装Bを使うか、実装Cを使うかをアノテーションによって変える」といったことができます。

```scala:BindingAnnotaion.scala
class ServiceUser @Inject() (@Named("realService") service: Service) {
  def use() { service.doSomething() }
}

class AnotherServiceUser @Inject() (@Named("fakeService") service: Service) {
  def use() { service.doSomething() }
}

class ServiceModule extends AbstractModule {
  override protected def configure() {
    bind(classOf[Service]).annotatedWith(Names.named("realService")).to(classOf[RealService])
    bind(classOf[Service]).annotatedWith(Names.named("fakeService")).to(classOf[FakeService])
  }
}
```

また、[カスタムのアノテーションを作る](https://github.com/google/guice/wiki/BindingAnnotations)こともできます。

実行例はこんな感じになります。

```scala
def run() {
  val injector = Guice.createInjector(new ServiceModule)
  val user = injector.getInstance(classOf[ServiceUser])
  val another = injector.getInstance(classOf[AnotherServiceUser])
  user.use() // => "RealService"
  another.use() // => "FakeService"
}
```

### Instance Bindings
ある型をある型にバインディングするのではなく、ある型に具体的なインスタンスをバンディングする機能です。`@Named`などと同時に使用することもできます。

```scala
class ServiceUser @Inject()(service: Service) {
  def use() {
    println("received: " + service)
  }
}

class ServiceModule extends AbstractModule {
  override protected def configure() {
    val service = new RealService
    println("bound: " + service)
    bind(classOf[Service]).toInstance(service)
  }
}
```

例によって`Injector`を準備してインスタンスを生成・実行してみると、確かに`.toInstance`で指定したインスタンスが`ServiceUser`に渡っていることが分かります。

```scala
def run() {
  val injector = Guice.createInjector(new ServiceModule)
  val user = injector.getInstance(classOf[ServiceUser])
  user.use()
}
```

```text:実行結果
bound: RealService@16f332ef
received: RealService@16f332ef
```

このバインディング方式は、複雑なオブジェクトの生成には向きません。
その場合には`@Provides`が推奨[^reco]されます。

[^reco]: [公式ドキュメント](https://github.com/google/guice/wiki/InstanceBindings)いわく、"Avoid using `.toInstance` with objects that are complicated to create, since it can slow down application startup. You can use an `@Provides` method instead."

### ProvidesMethods
オブジェクトの生成を`Module`内で実装できるようにします。`@Provides`アノテーションを付与することで、そのメソッドがインスタンス生成に利用されるようになります。

```scala
class ServiceUser @Inject()(service: Service) {
  def use() {
    service.doSomething()
  }
}

class ServiceModule extends AbstractModule {
  override protected def configure() {}

  @Provides
  def provideRealService(): Service = new RealService
}
```

なお、[プロバイダメソッドから例外を投げるのは推奨されない](https://github.com/google/guice/wiki/ProvidesMethods#throwing-exceptions)ようです。

### ProviderBindings
ProvidersMethodsがファクトリメソッドを作ってインスタンス生成を分離する機能だったのに対して、インスタンス生成を別クラスに分離するのがProviderBindingsです。より生成が複雑化した場合などに用いられるようです。

生成を行うクラスは、`Provider[T]`を実装するのがルールです。このクラスは`T`を返すメソッド`get()`を実装さえすれば機能します。

```scala
class RealServiceProvider extends Provider[Service] {
  override def get(): Service = new RealService
}
```

生成の委譲は`.toProvider`を記述することで行えます。

```scala
class ServiceModule extends AbstractModule {
  override protected def configure() {
    bind(classOf[Service]).toProvider(classOf[RealServiceProvider])
  }
}
```

### Constructor Bindings
型を型ではなく、型を指定したコンストラクタにバインドすることもできます。
例えば、依存性を注入する先のクラスのコンストラクタが複数ある場合に利用できます[^n-constructor]。
また、AOPなどでメタプログラミングをする場合にも必要なケースがあるとのことです。

[^n-constructor]: Guiceは依存性の解決時に、目標のクラスに複数コンストラクタがある場合、どのコンストラクタを使えばいのか判断できなくなります。単純な場合だと、`@Provides`などで生成を分離することでも対応できます。

```scala

class ServiceUser @Inject()(service: Service) {
  def use() {
    service.doSomething()
  }
}

class ServiceModule extends AbstractModule {
  override protected def configure() {
    bind(classOf[Service]).toConstructor(classOf[RealService].getConstructor())
  }
}
```

### Just-In-Time Bindings
ある型に対する実装が必要になったときでも、モジュールにバインドの記述がない場合、明示的バインディング(explicit bindings)ではなく暗黙のバインディング(Just-In-Time bindings / implicit bindings)が試みられます。

暗黙のバインディングには、明示的なコンストラクタと呼ばれるものと、`@ImplementedBy`を使うもの、`@ProvidedBy`を使うものがあります。

#### 明示的コンストラクタ
```scala
class RealService (@Named("serviceName") name: String) extends Service {
  override def doSomething(): Unit = println("service name is " + name)
}

class ServiceUser @Inject()(service: Service) {
  def use() {
    service.doSomething()
  }
}

class ServiceModule extends AbstractModule {
  override protected def configure(): Unit = {
    bind(classOf[Service]).to(classOf[RealService])
  }

  @Provides
  @Named("serviceName")
  def provideServiceName: String = "ServiceName!!"
}
```
この例では、`RealService`についてのバインディングが`ServiceModule`に明示的に記述されているわけではないにもかかわらず、`RealService`インスタンスへ`name`が注入される形で全体のインスタンス化が行われます。

対象のクラスは、publicなコンストラクタを1つのみ持ち、かつコンストラクタ引数はたかだか1つ（0 or 1）でなければならないです。

#### `@ImplementedBy`
抽象に対するデフォルト実装をアノテーションで指定することができます。

```scala
@ImplementedBy(classOf[AnotherFakeService])
trait AnotherService {
  def doSomething(): Unit
}

class AnotherFakeService extends AnotherService {
  override def doSomething(): Unit = println("AnotherFakeService")
}

class ServiceModule extends AbstractModule {
  override protected def configure() {}
}
```

上記コードを実行する実装を示します。

```scala
def run() {
  val injector = Guice.createInjector(new AbstractModule {
    override def configure(): Unit = {}
  })
  val service = injector.getInstance(classOf[AnotherService])
  service.doSomething() // => "AnotherFakeService"
}
```

つまり、`Module`に`bind(〜).to(...)`で実装を指定するのと同様の効果をインタフェースへの指定で実現できます。ただし、`bind`で指定された明示的なバインディングがある場合には、そちらが優先されます。

#### `@ProvidedBy`
`@ImplementedBy`が具体的なデフォルト実装を指定するものだったのに、対して、`@ProvidedBy`はデフォルトのプロバイダを指定します。`Module`に`bind(〜).toProvider(...)`指定をするのに相当します。明示的なバインディングがある場合はそちらが優先されるのも同様です。

```scala
@ProvidedBy(classOf[AnotherRealServiceProvider])
trait AnotherService {
  def doSomething(): Unit
}

class AnotherRealService extends AnotherService {
  override def doSomething(): Unit = println("AnotherRealService")
}

class AnotherRealServiceProvider extends Provider[AnotherService] {
  override def get(): AnotherService = new AnotherRealService
}
```

### Untargeted Bindings
バインドする先を指定しない`bind`を記述することができます。Guiceにバインドがあることを伝えられるので、依存関係をeagerに解決することができるようになります。

もちろん、通常は別の方法（例えば`@ImplementedBy`）で依存関係が記述されている事が前提です。

```scala
bind(classOf[Service]) // .toがないのでuntargeted binding
bind(classOf[Service]).in(classOf[Singleton]) // スコープ指定（後述）をすることができる
```

### Built-in Bindings
`java.util.logging.Logger`など、いくつかGuiceが組み込みで把握しているバインディングがあります。詳細についてはドキュメントを参照してください。

https://github.com/google/guice/wiki/BuiltInBindings

## スコープ
オブジェクトの生存区間(lifetime)を表すために、Guiceでは**スコープ**が導入されています。スコープの指定に従って、Guiceは新しくインスタンスを生成するか、使い回しをするか決めます。

スコープを指定するアノテーションには`@Singleton`、`@SessionScoped`、`@RequestScoped`があり、実装クラスにアノテーションすることでスコープを指定できます。それぞれがアプリケーション、セッション、リクエストを範囲とした生存区間を持ちます。

アノテーションの適用例です。

```scala
@Singleton
class RealService extends Service {
  def doSomething() {
    println("This is RealService")
  }
}
```

`bind`の際に、`.in`による指定も可能です。

```scala
bind(classOf[Service]).to(classOf[RealService]).in(classOf[Singleton])
```

ちなみに、[スコープはカスタムする事も可能そう](https://github.com/google/guice/wiki/Scopes)です。

### Stageの切り替え
インスタンスの生成をeagerに行うか、lazyに行うかを、**ステージ**と呼ばれる設定項目を切り替えることで変更できます。

ステージには`PRODUCTION`と`DEVELOPMENT`があります。開発時にはコンパイルしてから動作するまでの時間を短くするために、lazyなロードが向いています。一方でプロダクション動作の際にはeagerなロードが行われるようになっています。

ステージは、`Guice.createInjector`時に引数として`Stage`を渡すことで指定することができます。

```scala
Guice.createInjector(Stage.PRODUCTION, module)
```

### EagerなSingleton

バインド時に`.asEagerSingleton()`を指定することで、ステージの設定にかかわらず、eagerにオブジェクトをロードさせることができます。

```scala
bind(classOf[Service]).to(classOf[RealService]).asEagerSingleton()
```

## JSR-330との関係
Guiceが提供するアノテーションと、JSR-330[^jsr330]で定義されている`@Inject`などのアノテーションには微妙に違いがあるため、importを間違えるとうまく動かなくなることがあります。

特に注意が必要なのは`@Inject`とProvider周りです。名前が同じで振る舞いが微妙に異なるからです。詳細はGuice公式のドキュメントを参照してください。

https://github.com/google/guice/wiki/JSR330

[^jsr330]: JSR-330はDIについてのJava標準です。

## 参考資料
- https://github.com/google/guice/wiki