斯卡拉玩2.5和依赖注入演员

时间:2017-03-22 14:45:04

标签: scala dependency-injection playframework akka

我对播放文档https://www.playframework.com/documentation/2.5.x/ScalaAkka

有点困惑

查看示例,可以从控制器启动一个actor:

import play.api.mvc._
import akka.actor._ 
import javax.inject._

@Singleton
class Application @Inject() (system: ActorSystem) extends Controller {

  val actor = system.actorOf(Props(classOf[AnActor], "anActor")

  //...
}

或者可以依靠Guice来实例化那个演员

import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport

import actors.ConfiguredActor

class MyModule extends AbstractModule with AkkaGuiceSupport {
    def configure = {
        bindActor[AnActor]("anActor")
    }
}

当演员被Guice实例化时,可以向其中注入依赖

import akka.actor._
import javax.inject._
import play.api.Configuration

class AnActor @Inject() (configuration: Configuration) extends Actor {
    //...
}

但是,从控制器启动该actor会引发

[IllegalArgumentException: no matching constructor found on class AnActor for arguments []]

有没有办法将服务注入非Guice实例化的actor?

1 个答案:

答案 0 :(得分:1)

您可以使用bindActorFactory然后使用工厂在控制器中创建actor。

  

绑定演员工厂。当你想要注入子actor并希望将参数传递给它们时,以及让Guice提供一些参数时,这非常有用。

你在演员的伴侣对象中找到了一个工厂。

import akka.actor._

object AnActor {

  trait Factory {
    def apply(): Actor
  }
}

class AnActor @Inject() (configuration: Configuration) extends Actor {

  def receive = ???

}

您在模块中使用bindActorFactory

import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport

import actors._

class MyModule extends AbstractModule with AkkaGuiceSupport {
  def configure = {
    bindActorFactory[AnActor, AnActor.Factory]
  }
}

然后,你可以使用actor系统从你的控制器中实例化你的actor(所以他们将成为/ user actor的孩子)并且guice会发挥其魔力。

class LocationsController @Inject()(actorSystem: ActorSystem){

  def injectedChild2(create: => Actor, name: String, props: Props => Props = identity)(implicit system: ActorSystem): ActorRef = {
    system.actorOf(props(Props(create)), name)
  }

  val actor1: ActorRef = injectedChild2(childFactory(),"anActor1")(actorSystem)
  val actor2: ActorRef = injectedChild2(childFactory(),"anActor2")(actorSystem)

}

如果您想传递未由guice注入的参数,可以向Factory.apply添加参数,在这种情况下,您需要使用@Assisted

注释此参数
object AnActor {

  trait Factory {
    def apply(someValue: String): Actor
  }
}

class AnActor @Inject() (configuration: Configuration,@Assisted someValue: String) extends Actor {

  def receive = ???

}

class LocationsController @Inject()(actorSystem: ActorSystem){

  def injectedChild2(create: => Actor, name: String, props: Props => Props = identity)(implicit system: ActorSystem): ActorRef = {
    system.actorOf(props(Props(create)), name)
  }

  val actor1: ActorRef = injectedChild2(childFactory("value fom actor 1"),"anActor1")(actorSystem)
  val actor2: ActorRef = injectedChild2(childFactory("value from actor 2"),"anActor2")(actorSystem)

}

示例和方法injectedChild2取自original Play example,但已修改为从控制器创建actor。

<强>更新

检查this answer以了解为什么你应该避免使用控制器创建演员,用James Roper的话说。