Play2.4のDIについて動作確認（Guiceの使い方）

# はじめに
Play2.4でGuiceをつかったDIが出来るようになった。
簡単に使い方について動作確認を行った。

# DIって？
そもそもDIって何よ？な場合はこちら。
newによって生成するような場合とFactoryメソッドパターンで生成する場合とDIで生成する場合とのコードの違いなどが例示されている。

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

# 動作確認
## ベースコントローラの作成
先に ```activator new``` でmysampleという名前でテンプレートに基づいたアプリを作っておく。
今後、このコントローラを編集していく。

```scala:app/controllers/Application.scala
class Application extends Controller {
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }
}
```

## ControllerにServiceをInject
Serviceトレイトおよびその実装クラスを作成。servicesパッケージを作成し、その下に配置。

```scala:app/services/MyService.scala
trait MyService {
  def run(): String
}

class MyServiceImpl extends MyService {
  def run(): String = "MyServiceImpl!"
}
```

Controllerを修正。

```scala:app/controllers/Application.scala
class Application @Inject() (service: MyService) extends Controller {

  def index = Action {
    println(service.run())
    Ok(views.html.index("Your new application is ready."))
  }

}
```

続いてroutesを修正。
先頭に@を付与する。

```scala:conf/routes
GET     /                           @controllers.Application.index
```

MyServiceトレイトの実装クラスとして何を使うのかを宣言する。
modulesパッケージを作成し、AbstractModuleを継承したクラスをその下に配置。

```scala:modules/MyServiceModule.scala
class MyServiceModule extends AbstractModule {
  override def configure(): Unit = 
    bind(classOf[MyService]).to(classOf[MyServiceImpl])
}
```

最後に、application.confにmodulesの定義を追加。

```:conf/application.conf
play.modules.enabled += "modules.MyServiceModule"
```

```http://localhost:9000``` にアクセス。コンソールに ```MyServiceImpl!``` が表示されていることを確認。

なお、printlnするときに ```println(this)``` とかやるとわかりますが、アクセスが有る度にインスタンスが生成されていることが確認できます。
都度newされるのを抑制するには ```@Singleton``` アノテーションを付与することで実現できます。

```scala:app/controllers/Application.scala
@Singleton
class Application @Inject() (service: MyService) extends Controller {
```

## アノテーションを使って同じことをする
上記と同様のことをアノテーションを使って実現する。

Serviceトレイトおよびその実装クラスは同じものを使用するが、traitに ```@ImplementedBy``` アノテーションを使ってこのトレイトの実装クラスを宣言する。

```scala:app/services/MyService.scala
@ImplementedBy(classOf[MyServiceImpl])
trait MyService {
  def run(): String
}

class MyServiceImpl extends MyService {
  def run() = "MyServiceImpl!"
}
```

先の例との違いは、以下２点となる。

- MyServiceModule.scalaを必要としない
- application.confに ```play.modules.enabled``` の追加も必要としない

## 実装クラスの切り替え（その１）
とはいえ、普通はトレイトを切るからには実装クラスは２つ以上無いと面白く無い。
MyService.scalaに実装クラスMyServiceImpl2を追加する。
なおこの時、先の例で追加した ```@Implemented``` アノテーションはMyServiceトレイトからは削除しておく。

```scala:app/services/MyService.scala
class MyServiceImpl2 extends MyService {
  def run() = "MyServiceImpl 2!"
}
```

改めてMyServiceModuleを定義する。

```scala:app/modules/MyServiceModule.scala
class MyServiceModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[MyService])
      .annotatedWith(Names.named("sono1"))
      .to(classOf[MyServiceImpl])

    bind(classOf[MyService])
      .annotatedWith(Names.named("sono2"))
      .to(classOf[MyServiceImpl2])
  }
```

で、Applicationコントローラを以下のように変える。
@Namedの引数をsono1やsono2に変えることで切り替え可能。

```scala:app/controllers/Application.scala
class Application @Inject() (@Named("sono2") service: MyService) extends Controller {
```

## 実装クラスの切り替えその２
テスト実施時と本番時で実装クラスを切り替える場合を想定する。

MyServiceトレイトをこのように変更する。

```scala:app/services/MyService.scala
@ProvidedBy(classOf[MyServiceProvider])
trait MyService {
  def run(): String
}
```

MyServiceProviderを実装する。
Environmentは特にModuleとかを用意しなくても標準で提供される。

```scala:app/providers/MyServiceProvider.scala
class MyServiceProvider @Inject() (env: Environment) extends Provider[MyService] {
  override def get(): MyService = {
    if (env.mode == Mode.Prod) new MyServiceImpl()
    else new MyServiceImpl2()
  }
}
```

以上。
Moduleの時のようにapplication.confへの追記は不要。
これで起動モードで実装クラスを切り替えることが出来た。

## 必ずしもtraitおよびその実装クラスを用意しなくてもよい
String型やInt型に具体的な値を適用する。

```scala:app/controllers/Application.scala
class Application @Inject() (@Named("sono1") service: MyService, @Named("num2") num: Int) extends Controller {

  def index = Action {
    println(service.run() * num)
    Ok(views.html.index("Your new application is ready."))
  }

}
```

新たなModuleを定義。

```scala:app/modules/MyNumModule.scala
class MyNumModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[Int])
      .annotatedWith(Names.named("num1"))
      .toInstance(1)

    bind(classOf[Int])
      .annotatedWith(Names.named("num2"))
      .toInstance(2)
  }
}
```

最後にapplication.confへ追記。

```:conf/application.conf
play.modules.enabled += "modules.MyServiceModule"
play.modules.enabled += "modules.MyNumModule"
```

これで実行すると、MyServiceImpl!が２回表示される。

# 参考資料

- https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
- https://github.com/google/guice/wiki/Bindings