Play 2.4のDependency Injectionを試してみた

Play 2.4にはGuiceを使ったJSR 330ベースのDI機能が導入されており、コントローラに対してコンポーネントを注入することができるようになっています。Playの標準機能もDI前提のAPIに移行しており、Play 2.4を使用する上では必須っぽい感じになっています。というわけで今日はこのDI機能を少し触ってみました。

まず、DIを使用するために従来はobjectとして実装する必要のあったコントローラをclassとして実装するようになっています。ただし、build.sbtの以下の行を削除すると従来通りobjectとしてコントローラを実装することができます。

routesGenerator := InjectedRoutesGenerator

この場合は逆にDIが使用できなくなるわけですが、routesで以下のように@をつけるとそのコントローラのみDIを利用可能なclassとして実装できるようです。Play 2.3から少しずつ移行する場合は便利かもしれません。

GET    /some/path    @controllers.Application.index

肝心のDIですが、以下のようにコントローラのコンストラクタ@Injectアノテーションを付与します。

class UserController @Inject()(
  val dbConfigProvider: DatabaseConfigProvider
) extends Controller 
  with HasDatabaseConfigProvider[JdbcProfile] {
  ...
}

もちろん自分で作ったクラスをDIすることもできますし、コントローラに対してDIするだけでなく、DIされるクラスに対して@Injectアノテーションを付けておけば芋づる式にDIすることもできます。

class SampleDao @Inject()(
  val dbConfigProvider: DatabaseConfigProvider
) extends HasDatabaseConfigProvider[JdbcProfile] {
  ...
}

class UserController @Inject()(sampleDao: SampleDao){
  ...
}

DIされるクラスはデフォルトではprototype(DIされるたびに毎回インスタンスが生成される)ですが、@Singletonアノテーションを付けておくことでシングルトンにすることもできます。

@Singleton
class SampleDao ...

また、GuiceのModuleを作ってapplication.confに以下のように書いておくとPlayが自動的に使ってくれるみたいです。バインディングの細かい制御やカスタマイズを行いたい場合は必要になりそうです。

play.modules.enabled += "modules.HelloModule"

しかし言語レベルでobjectとか用意されているのに@Singletonつけたり、JavaアノテーションGuiceの使い方を覚えないといけなかったりなど、なんだか違和感がありまくりですね。また、Slickが3.0でモナモナしているのにPlayはGuiceでDIとか、プロダクトの整合性が取れているようで取れていない気がするのは気のせいでしょうか…。

Play 2.4ではこの他にもコンパイル時DIの機能があるみたいです。まだ自分で試せていないのですが、こちらの記事がわかりやすいです。