将构造函数参数传递给 Play 管理的 Actor

Passing a constructor argument to a Play managed Actor

我有一个主管和一个工人演员。工作人员调用服务。所有这些都由 Play(/Guice) 管理。

这里是代码的简化:

class SupervisorActor @Inject()(workerFactory: WorkerActor.Factory, serviceA: AService)
    extends Actor
    with InjectedActorSupport {

  override def receive: Receive = {
    case message => injectedChild(workerFactory(serviceA), "childActorName").forward(message)
  }

}

class WorkerActor @Inject()(serviceA: AService) extends Actor {
  override def receive: Receive = {
    case _ => println(serviceA.getMessage())
  }
}

object WorkerActor {
  trait Factory {
    def apply(serviceA: AService): Actor
  }
}

@ImplementedBy(classOf[AServiceImpl])
trait AService {
  def getMessage(): String
}

class AServiceImpl @Inject()(configuration: Configuration) extends AService {
  override def getMessage(): String = configuration.get[String]("id1")
}

Module.scala(configure方法):

bindActor[SupervisorActor]("supervisor")
bindActorFactory[WorkerActor, WorkerActor.Factory]

一切正常。到目前为止,每个 class.

都有一个实例

现在,AServiceImpl.getMessage() 需要查找动态字符串(不只是每次 "id1")。所以改成这样:

class AServiceImpl @Inject()(configuration: Configuration, propertyId: String) extends AService {
  override def getMessage(): String = configuration.get[String](propertyId)
}

并从 trait AService 中删除了 @ImplementedBy(classOf[AServiceImpl])

我们的想法是实例化 supervisor、worker 和 service 的 2 个实例,并将它们连接在一起 Module.scala。所以我从 Module.scala:

开始
val aServive: AService = new AServiceImpl(configuration, "id1")

现在的挑战是将这个实例注入主管。

感谢对这些的任何帮助:

  1. 当注入器仍在构建中时,如何在 configure() (Module.scala) 中访问 Guice managed WorkerActor.Factory?
  2. 我尝试使用带有 WorkerActor.Factory 作为参数的 @Provides 方法。但是 bindActor() 调用只允许在 configure() 中使用。不在@Provides 方法中。
  3. 如何让 Play 注入 workerFactory: WorkerActor.Factory 但提供我自己的 AService?据我所知,bindActor(...)似乎不​​支持这个。

编辑:

我的解决方案基于以下 Renato 的回答以供将来使用:

我必须为每个需要的 actor(对象图)实例实现一个 ActorRef Provider。像这样:

class PropertyId1SupervisorActorProvider @Inject()(configuration: Configuration,
                                                   workerFactory: WorkerActor.Factory,
                                                   actorSystem: ActorSystem)
    extends Provider[ActorRef] {

  override def get(): ActorRef = {
    val aService: AService = new AServiceImpl(configuration, "propertyId1")
    actorSystem.actorOf(Props(classOf[SupervisorActor], workerFactory, aService))
  }
}

然后在Module.scala中绑定这个提供者:

bind(classOf[ActorRef])
      .annotatedWith(Names.named("propertyId1Actor"))
      .toProvider(classOf[PropertyId1SupervisorActorProvider])

这确实需要一些工作才能让另一个实例运行。但这是我目前拥有的最佳解决方案。如果有改进的建议,我会洗耳恭听。

我认为您可以通过使用一个提供程序来简化它,该提供程序可以完成所有工作,而不必过多依赖任何 Guice 连接。提供者应该 return 您想要注入系统其他部分的 Actor。 Actor 下的所有图形都可以而且应该隐藏,换句话说,不要暴露给 DI。

只需为 actor 创建一个提供者,注入 ActorSystemConfiguration,这样您就可以创建 actor 并构建服务实例。

由于您需要该 actor 的两个实例,因此需要为它们取不同的名称,并在 Guice 中用不同的名称绑定它们。