【问题标题】:Passing a constructor argument to a Play managed Actor将构造函数参数传递给 Play 托管 Actor
【发布时间】:2020-03-31 22:55:53
【问题描述】:

我有一个主管和一个工人演员。工人调用服务。所有这些都由 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]

一切都很完美。到目前为止,每个类都有一个实例。

现在,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])

这个想法是实例化主管、工作人员和服务的 2 个实例,并在 Module.scala 中将它们连接在一起。所以我从 Module.scala 开始:

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

现在的挑战是将这个实例注入到 Supervisor 中。

感谢您在这些方面的任何帮助:

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

编辑:

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

我必须为所需的每个参与者实例(对象图)实现一个 ActorRef 提供程序。像这样:

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中绑定这个provider:

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

这确实需要一些工作才能让另一个实例运行。但这是我目前最好的解决方案。如果有关于改进这一点的建议,我会全力以赴。

【问题讨论】:

    标签: scala playframework akka


    【解决方案1】:

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

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

    由于您需要拥有该参与者的两个实例,因此需要为它们设置不同的名称,并在 Guice 中使用不同的名称绑定它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2015-10-11
      • 2013-02-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多