Play framework 2.0 betaが出たようです6 – Dependency Injectionをやってみよう

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

PlayでDependency injectionを実現する

先日Play framework2.0でおもしろそうなtipsがblogで紹介されていたので、ここでもご紹介。

そのブログ

内容はPlay framework2.0(Scala)でDIを実現しようというものです。

近年のJava系Webアプリケーションで、DIはあたりまえのように使用されていると思います。
Play frameworkにもSpringやGuiceを使用するためのモジュールがありますが、独自にDIの仕組みをもっているわけではありません。
しかし、今回ご紹介するtipsではPlay framework2.0とScala標準の機能(とデザインパターン)でDIを実現しています。
ではblogの内容にそってご紹介させていただきます。

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.7.2
  • Playframework : 2.0β
  • Java : 1.6.0_26

注意:
この記事ではβ版を使用しています。
正式リリース前の状況のため思わぬ不具合が生じるかもしれませんので、ご了承ください。

サンプル作成

ではDIを試してみるためのプロジェクトを作成しましょう。元のblogではDIで認証用トレイトを本番用、モック用と切り替える例を書いてますが、
本記事では単純にprintlnで異なるメッセージを出すだけにします。

プロジェクトを作成

play newコマンドを使用し、Scalaベースでのプロジェクトを作成しましょう。

% play new depSample

Application.scala修正

まずは生成されたApplication.scalaの修正を行います。
本来ここにはApplicationコントローラーオブジェクトが定義されていますが、下記のように修正し、traitを追加します。
 

package myApp

import play.api._
import play.api.mvc._

trait Application extends Controller {
  self:Log =>
  
  def index = Action {
    out()
    Ok(views.html.index("Your new application is ready."))
  }
  
}

trait Log {
  def out():Unit
}

trait DevLog extends Log {
  override def out():Unit = println("dev mode now!")
}

trait ProdLog extends Log {
  override def out():Unit = println("prod mode now!")
}

Logトレイトとそれを継承したトレイトが、今回DIで切り替える処理です。
パッケージを変更してから元々objectであるApplicationをtraitとして定義し、さらにself typeアノテーションでLogトレイトを宣言します。
inxex関数ではLogトレイトのout関数を呼んでいますが、まだDevLogかProdLogの処理が実行されるかは決定していません。

package.scalaを作成

次に、Application.scalaと同じディレクトリにpackage.scalaという名前でパッケージオブジェクトを作成します。

package object myApp {
  
  import play.api.Play._
  
  val Application = if (isDev) {
    new myApp.Application with myApp.DevLog
  } else {
    new myApp.Application with myApp.ProdLog
  }
}

この処理は初回アクセス時に一度だけ実行されます。Play.isDev関数を呼んでDEVモードかどうか判定し、
もしDEVモードで起動されれば、DevLogトレイトを、そうでなければProdLogトレイトをmix-inしてApplicaionトレイトをインスタンス化します。

routesファイルの修正

最後にroutesを修正します。

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           myApp.Application.index()

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

動作確認

では起動して動作を確認してみましょう。
アプリケーションをDEVモードで起動(普通に起動)してlocalhost:9000にアクセスしてみてください。
コンソールには「dev mode now!」と表示されます。

%play                                     
・・・・・・・・・・・
[depend] $ compile
[success] Total time: 0 s, completed 2011/11/29 17:41:56
[depend] $ run

--- (Running the application from SBT, auto-reloading is enabled) ---

[info] play - Listening for HTTP on port 9000...

(Server started, use Ctrl+D to stop and go back to the console...)

まとめ

今回はここの記事を元にDIを実現するサンプルを紹介しました。
PlayとScalaの基本的な機能を組み合わせるだけでDIが実装できるのはすごいと思います。
play2.0 + scalaの組み合わせは、柔軟性がすごい高そうですね。 元のサイトではさらに詳細な説明があるので確認してみてください。