LoginSignup
18
9

More than 5 years have passed since last update.

Play Frameworkで起動時に処理を実行する

Last updated at Posted at 2017-03-26

データの初期化とかタイマーみたいな常駐機能の起動とか、アプリケーションの起動時に実施したいものってありますよね。
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!

18
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
9