【问题标题】:scala play 2.5 and dependency injection into actorscala play 2.5和依赖注入到actor
【发布时间】:2017-03-22 14:45:03
【问题描述】:

我对播放文档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 {
    //...
}

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

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

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

【问题讨论】:

  • 能否将Configuration注入Application,然后作为参数提供?然后,演员创作将变成类似val actor = system.actorOf(Props(new AnActor(configuration)), "anActor")
  • 是的,这就是我目前所做的。然而,这似乎不是很优雅。此外,我的最终目标是将服务注入到子actor中,我希望避免将它们作为参数从构造函数传递给构造函数。
  • 我明白了。我不确定这是否可行,但AssistedInject 可能会有所帮助。

标签: scala dependency-injection playframework akka


【解决方案1】:

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

绑定一个actor工厂。当您希望注入子actors,并希望将参数传递给它们,以及让Guice 提供一些参数时,这很有用。

你在 Actor 的伴生对象中添加了一个工厂。

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 的话来说。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    相关资源
    最近更新 更多