CDIの参照実装のWeldですが、Java SE環境でも使えるんですね。どこかで聞いたことがあった気がしますが、忘れてました…。
Weld
http://weld.cdi-spec.org/
そんなに情報はないですが、オフィシャルのドキュメントと
18.4. Java SE
http://docs.jboss.org/weld/reference/latest/en-US/html_single/#d0e5666
こちらを参考にさせていただきました。
JavaSE 環境でCDI(Weld)を使う
http://qiita.com/opengl-8080/items/2f03ab496e871cf32f54
準備
依存関係の定義。
build.sbt
name := "weld-se-example" version := "0.0.1-SNAPSHOT" scalaVersion := "2.10.3" organization := "org.littlewings" scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked") libraryDependencies ++= Seq( "org.jboss.weld.se" % "weld-se" % "2.1.2.Final", "org.scalatest" %% "scalatest" % "2.0" % "test" )
オフィシャルサイトでは、2.2.0.Alpha1が最新だよと書いていましたが、アルファ版なので今回は2.1.2.Finalを使うようにしました。ScalaTestはテストコード用ですね。
BeanとCDIの有効化
Beanとしては、こんなのを用意。
src/main/scala/org/littlewings/weldse/example/CalcService.scala
package org.littlewings.weldse.example class CalcService { def plus(left: Int, right: Int): Int = left + right }
src/main/scala/org/littlewings/weldse/example/DateService.scala
package org.littlewings.weldse.example import java.util.Date import javax.enterprise.context.ApplicationScoped @ApplicationScoped class DateService { private[this] val thisTime: Date = new Date def time: Date = thisTime }
こちらは、@ApplicationScopedアノテーション付き。
src/main/scala/org/littlewings/weldse/example/FacadeService.scala
package org.littlewings.weldse.example import javax.inject.Inject class FacadeService { @Inject var calcService: CalcService = _ @Inject var dateService: DateService = _ }
こちらは、@Injectで他のBeanを注入するパターン。
あと、中身は空でよいのでCDI有効化のため、以下のファイルを作成します。
src/main/resources/META-INF/beans.xml
では、動かしてみましょう。
動作確認
動作確認は、ScalaTestを使った単体テストコードで行います。
以下のような雛形を用意。
src/test/scala/org/littlewings/weldse/example/WeldSeSpec.scala
package org.littlewings.weldse.example import javax.enterprise.inject.Instance import org.jboss.weld.environment.se.{Weld, WeldContainer} import org.scalatest.FunSpec import org.scalatest.Matchers._ class WeldExample extends FunSpec { describe("weld-se spec") { // ここに、テストコードを書く!! } }
まずは、単純な使い方。
it("simple use") { val weld: Weld = new Weld val container: WeldContainer = weld.initialize() val instance: Instance[AnyRef] = container.instance val calcService = instance.select(classOf[CalcService]).get calcService.plus(1, 3) should be (4) weld.shutdown() }
Weldクラスをnewして、その後WeldContainer、Instanceと取得。Instance#selectからgetすることで、目的のBeanのインスタンスを取得することができます。Instance#select時にはQualifierを渡せそうな感じですね。
最後に、Weldをシャットダウンします。
で、続けて他のテストを。
取得したCalcServiceのインスタンスが、それぞれ別のインスタンスであることの確認。
it("normal") { val weld: Weld = new Weld val container: WeldContainer = weld.initialize() val instance: Instance[AnyRef] = container.instance val calcService1 = instance.select(classOf[CalcService]).get val calcService2 = instance.select(classOf[CalcService]).get calcService1 eq calcService2 should be (false) weld.shutdown() }
@ApplicationScopedアノテーションを付与したDateServiceについては、同じインスタンスが取れることの確認。
it("application-scoped") { val weld: Weld = new Weld val container: WeldContainer = weld.initialize() val dateService1 = container.instance.select(classOf[DateService]).get val dateService2 = container.instance.select(classOf[DateService]).get dateService1 should be theSameInstanceAs dateService2 dateService1.time should be (dateService2.time) weld.shutdown() }
@Injectアノテーションによる、依存関係の解決ができていることの確認。
it("@Inject resolved") { val weld: Weld = new Weld val container: WeldContainer = weld.initialize() val instance: Instance[AnyRef] = container.instance val facadeService = instance.select(classOf[FacadeService]).get val dateService = instance.select(classOf[DateService]).get facadeService.dateService should be theSameInstanceAs dateService facadeService.dateService.time should be (dateService.time) facadeService.calcService.plus(1, 5) should be (6) weld.shutdown() }
唯一ハマった点は、最初beans.xmlをsrc/main/resources/META-INFではなくて、src/test/resources/META-INFに作ってしまってBeanが見つけられなかったことくらいですね。単にクラスパスが通ってればいい、というわけではないことがちょっと分かりました。
今回書いたサンプルは、こちらにアップしています。
https://github.com/kazuhira-r/javaee6-scala-examples/tree/master/weld-se-example