Play2.4 の DI を弄ってみる
まず適当に Singleton アノテーションをくっつけて使用する。
package utils.modules import javax.inject._ /** * アクセスカウンター */ @Singleton class AccessCounter { @volatile private var price = 0 def add(p: Int) = price += p def get = price }
そしたらコントローラでそれを Inject で利用する。
class Application @Inject() (accessCounter: AccessCounter) extends Controller { def index = Action { accessCounter.add(1) Ok(views.html.index(s"CurrentCount is ${accessCounter.get}.")) } }
凄まじく単純な使い方だ。
次は定義分離。
package utils.modules import com.google.inject.ImplementedBy @ImplementedBy(classOf[EnglishHello]) trait Hello { def sayHello(name: String): String } class EnglishHello extends Hello { def sayHello(name: String) = "Hello " + name }
そして使ってみる。
class Application @Inject() (hello: Hello) extends Controller { def index = Action { Ok(views.html.index(s"CurrentCount is ${hello.sayHello("SOX!")}.")) } }
ネタに関しては突っ込まないで。
そして追加の DI を定義してみる。
追加定義。
class JaHello extends Hello { override def sayHello(name: String): String = "チョリーッス! " + name }
これを使い分ける定義を記載する。
package utils.modules import com.google.inject.AbstractModule import com.google.inject.name.Names class HelloModule extends AbstractModule { def configure() = { bind(classOf[Hello]) .annotatedWith(Names.named("en")) .to(classOf[EnglishHello]) bind(classOf[Hello]) .annotatedWith(Names.named("ja")) .to(classOf[JaHello]) } }
そして設定を conf/application.conf
で読み込む。
play.modules.enabled += "utils.modules.HelloModule"
これで使用準備 OK。
@Named
アノテーションで日本語版の使用を指定する。
import javax.inject.{Named, Inject} class Application @Inject() (@Named("ja") hello: Hello) extends Controller { def index = Action { Ok(views.html.index(s"CurrentCount is ${hello.sayHello("SOX!")}.")) } }
もう少しシンプルな定義が用意されているらしく、下記のようにも書ける。
import play.api.{Configuration, Environment} import play.api.inject._ class HelloModule extends Module { def bindings(environment: Environment, configuration: Configuration) = Seq( bind[Hello].qualifiedWith("en").to[EnglishHello], bind[Hello].qualifiedWith("de").to[GermanHello] ) }
で、バインディングのデフォルト設定を書き換える場合、下記のように定義する。
import play.api.inject._ class HelloModule extends Module { def bindings(environment: Environment, configuration: Configuration) = Seq( bind[Hello].to[JaHello], bind[Hello].qualifiedWith("en").to[EnglishHello] ) }
こうすると、controller 側の @Named("ja")
を引っこ抜いても日本語がデフォルトのバインディングになる。
それを踏まえた上で、 "Guice公式":https://github.com/google/guice/wiki/Scopes を眺めると、toProvider
構文があるのがわかる。
そしてよくよく思い出せば http://white-azalea.hatenablog.jp/entry/2015/08/23/180520 で貼り付けたリンク先。
「SecurityHeadersConfigProvider」何てものがあるじゃないか?
だが、現状ではうまく行ってない。
調べた所までメモ。
こんな定義を作って
@Singleton class MySecurityHeadersConfigProvider @Inject() (configuration: Configuration) extends Provider[SecurityHeadersConfig] { lazy val get = SecurityHeadersConfig.fromConfiguration( configuration.++(Configuration("play" -> MySecurityHeadersConfigProvider.values))) } object MySecurityHeadersConfigProvider { val values = Map( "filters" -> Map( "headers" -> Map( "frameOptions" -> "SAMEORIGIN" ) ) ) }
こんなことをしてみたが、実行時エラー。
原因は、「A binding to play.filters.headers.SecurityHeadersConfig was already configured at play.filters.headers.SecurityHeadersModule.bindings(既にバインド設定されてるから設定できねーよ)」とのこと
class HelloModule extends Module { def bindings(environment: Environment, configuration: Configuration) = Seq( bind[SecurityHeadersConfig].toProvider[MySecurityHeadersConfigProvider], bind[Hello].to[JaHello], bind[Hello].qualifiedWith("en").to[EnglishHello] ) }
で、こんなものをを見つけたわけだが、
ここだけ Module 分離してみたものの、取り込みで挫折。
class CustomApplicationLoader extends GuiceApplicationLoader() { override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = { val extra = Configuration("a" -> 1) initialBuilder .in(context.environment) .loadConfig(extra ++ context.initialConfiguration) .overrides((overrides(context)): _*) // ←この辺に入れそうだけど、型定義が違う } }
って所で、疲れたので寝る。
記事の最初から最後までのトータル時間 3 時間…流石に集中力切れたよ。