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の機能があるみたいです。まだ自分で試せていないのですが、こちらの記事がわかりやすいです。