データの初期化とかタイマーみたいな常駐機能の起動とか、アプリケーションの起動時に実施したいものってありますよね。
JavaEEの場合は「Servletのinitメソッド」や「CDIのApplicationスコープ + @Initialized」あたりを使うことが多いと思います。
Playの場合、昔はGlobalSettings#onStart
ってので対応してたみたいだけど、2.5ではDI(dependency injection)を使って何とかしろって方向見たいです。
というか、この辺の話がDIは「デフォルトでGuiceを使ってるからGuice知ってれば分かるでしょ?」的なノリなのか、さっぱり書いてないです。段々Playの流儀に慣れてきましたよ?
まあ、activatorで作るplay-seed(playのテンプレート)が良い見本になるので、まずは動かしてコード読めってことですね。
結論だけ先に書くと、app直下にGuiceのModuleをAbstractModuleを継承して作って、configureメソッドの中でインスタンスを生成してやればいいです。
class Module extends AbstractModule {
override def configure() = {
// application starts.
bind(classOf[ExampleService]).asEagerSingleton()
}
}
@Singleton
class ExampleService {
Logger.info("Hello World")
}
これで起動時にHello World
とログが出力されるようになりました。
モジュールによる起動時の読み込み
さて、ではなんでこれでOKかというと答えはapplication.conf
の中にあります。
play.modules {
# By default, Play will load any class called Module that is defined
# in the root package (the "app" directory), or you can define them
# explicitly below.
# If there are any built-in modules that you want to disable, you can list them here.
#enabled += my.application.Module
# If there are any built-in modules that you want to disable, you can list them here.
#disabled += ""
}
そもそもモジュールは独立したコンポーネントを提供して、Playをプラガブルに拡張できるようにするための機能です。
コミュニティベースとはいえ公式に載ってるものだけでもこれだけあります。
その性質上、モジュールは起動時に呼ばれないといけないので、
play.modules.enabled += "modules.MyModule"
で追記をしておくと起動時に読み込んでくれるようです。
さらにコメントに記載の通り、app直下にあるModule
という名前のモジュールクラスはデフォルトで読み込むようです。
なので、Moduleクラスになんか書いとけば実行時、より正確にはconfigのロード時に呼ばれることになります。
今回は後述するライフサイクル管理がしたいので、bindもしています。
GuiceによるBind
先ほどのモジュールですがGuiceのモジュールを使っています。GuiceはJSR 330: Dependency Injection for Javaの実装です。JavaEEのCDIをもうちょっと一般化したものですね。
使い方はいたって簡単で、最初のサンプルの通り必要であれば@Singleton
でスコープを定義して、ModuleのconfigureにBind定義を書くだけです。
Bindメソッドの使い方はこちらを参照。
Application life cycle
起動時だけではなく終了時もクリーンアップとか実行したいものです。少なくとも正常終了時は後片付けはしたいですね。
そんなときはApplicationLifecycleを使います。
@Singleton
class ExampleService @Inject()(appLifecycle: ApplicationLifecycle) {
Logger.info("start")
appLifecycle.addStopHook { () =>
Logger.info("end")
Future.successful(())
}
}
Moduleクラスの方の記述は変わりませんが、ExampleServiceにApplicationLifecycle
クラスをインジェクションしてこちらを利用します。
ApplicationLifecycleはその名の通りライフサイクルイベントを制御しているので、addStopHook
にて終了時に実行したいメソッドを登録できます。
これで、タイマーの終了処理とかクリーンアップとかリソース解放とかの後片付け処理もバッチりですね。
まとめ
Playの資料はネット上だと2.3が一番豊富な気がします。
ただ、2.4, 2.5でDI周りを中心に結構仕様が変わってるので要注意ですね。
マニュアルも目的の箇所だけ読んでも、良く分からないことが多いのでコード見るのも大事ですね。
それでは、Happy Hacking!