Scala / Akka / Guice动态注入儿童演员

我希望能够创建同一父actor的多个实例,但具有不同的子actor . 我认为这对Guice来说是可能的,但我还没有找到解决方案 .

这就是我的想法〜

Controller:

class Application @Inject()(@Named(ParentActor.parentActor1) parentActor1: ActorRef,
                        @Named(ParentActor.parentActor2) parentActor2: ActorRef)
  extends Controller {

  def index = Action {
    parentActor1 ! "Message"
    parentActor2 ! "Message"
    Ok()
  }
}

Parent Actor:

object ParentActor {
  final val parentActor1 = "parentActor1"
  final val parentActor2 = "parentActor2"
}

class ParentActor @Inject() (childActor: ActorRef) extends Actor {

  def receive = {
    case "Message" =>
      println(s"ParentActor ${self.path} received message...")
      childActor ! "Message"
  }
}

Child Actor A:

class ChildActorA extends Actor {

  def receive = {
    case "Message" =>
      println("ChildActorA received message...")
  }
}

Child Actor B:

class ChildActorB extends Actor {

  def receive = {
    case "Message" =>
      println("ChildActorB received message...")
  }
}

Module:

class Modules extends AbstractModule with AkkaGuiceSupport {

  override def configure() = {
    bindActor[ParentActor](ParentActor.parentActor1)
    bindActor[ParentActor](ParentActor.parentActor2)
  }
}

如果我希望“parentActor1”将其“childActor”引用指向ChildActorA的实例,并将“parentActor2”指向其“childActor”ref指向ChildActorB的实例,该怎么办?这可能与Guice一起实现吗?

回答(1)

2 years ago

我正在使用一些基于https://github.com/rocketraman/activator-akka-scala-guice的代码来完成类似的事情

我没有使用Play,所以我必须初始化Guice并引导actor系统

import akka.actor._
import javax.inject.{Inject, Provider, Singleton}
import com.google.inject.AbstractModule
import net.codingwell.scalaguice.InjectorExtensions._
import com.google.inject.Guice
import com.google.inject.Injector
import scala.concurrent.Await
import scala.concurrent.duration.Duration

object Bootstrap extends App {

  val injector = Guice.createInjector(
    new AkkaModule(),
    new ServiceModule()
  )

  implicit val system = injector.instance[ActorSystem]

  val parentActor1 = system.actorOf(ParentActor.props(ChildActorA.name))
  val parentActor2 = system.actorOf(ParentActor.props(ChildActorB.name))


  parentActor1 ! "Message"
  parentActor2 ! "Message"

  system.terminate()

  Await.result(system.whenTerminated, Duration.Inf)  
}

要初始化Guice,有两个类/对象:

一个用于初始化扩展并在需要时注入actor系统

import akka.actor.ActorSystem
import AkkaModule.ActorSystemProvider
import com.google.inject.{AbstractModule, Injector, Provider}
import com.typesafe.config.Config
import net.codingwell.scalaguice.ScalaModule
import javax.inject.Inject

object AkkaModule {

  class ActorSystemProvider @Inject() (val injector: Injector) extends Provider[ActorSystem] {

    override def get() = {

      val system = ActorSystem("actor-system")

      GuiceAkkaExtension(system).initialize(injector)

      system
    }
  }
}

class AkkaModule extends AbstractModule with ScalaModule {

  override def configure() {
    bind[ActorSystem].toProvider[ActorSystemProvider].asEagerSingleton()
  }

}

另一个为孩子们创建提供者

import javax.inject.Inject

import akka.actor.{Actor, ActorRef, ActorSystem}
import com.google.inject.name.{Named, Names}
import com.google.inject.{AbstractModule, Provides, Singleton}
import net.codingwell.scalaguice.ScalaModule

class ServiceModule extends AbstractModule with ScalaModule with GuiceAkkaActorRefProvider {

  override def configure() {
    bind[Actor].annotatedWith(Names.named(ChildActorA.name)).to[ChildActorA]
    bind[Actor].annotatedWith(Names.named(ChildActorB.name)).to[ChildActorB]
  }

  @Provides
  @Named(ChildActorA.name)
  def provideChildActorARef(@Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorA.name)

  @Provides
  @Named(ChildActorB.name)
  def provideChildActorBRef(@Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorB.name)

}

扩展名

import akka.actor._
import com.google.inject.Injector

class GuiceAkkaExtensionImpl extends Extension {

  private var injector: Injector = _

  def initialize(injector: Injector) {
    this.injector = injector
  }

  def props(actorName: String) = Props(classOf[GuiceActorProducer], injector, actorName)

}

object GuiceAkkaExtension extends ExtensionId[GuiceAkkaExtensionImpl] with ExtensionIdProvider {

  override def lookup() = GuiceAkkaExtension

  override def createExtension(system: ExtendedActorSystem) = new GuiceAkkaExtensionImpl

  override def get(system: ActorSystem): GuiceAkkaExtensionImpl = super.get(system)

}

trait NamedActor {
  def name: String
}

trait GuiceAkkaActorRefProvider {

  def propsFor(system: ActorSystem, name: String) = GuiceAkkaExtension(system).props(name)

  def provideActorRef(system: ActorSystem, name: String): ActorRef = system.actorOf(propsFor(system, name))

}

制片人

import akka.actor.{IndirectActorProducer, Actor}
import com.google.inject.name.Names
import com.google.inject.{Key, Injector}

class GuiceActorProducer(val injector: Injector, val actorName: String) extends IndirectActorProducer {

  override def actorClass = classOf[Actor]

  override def produce() = injector.getBinding(Key.get(classOf[Actor], Names.named(actorName))).getProvider.get()

}

和你的演员

import javax.inject.Inject
import akka.actor._


object ParentActor {

  def props(childName: String)(implicit @Inject() system: ActorSystem) = Props(classOf[ParentActor],system.actorOf(GuiceAkkaExtension(system).props(childName)))

}

class ParentActor (childActor: ActorRef) extends Actor {

  def receive = {
    case "Message" =>
      println(s"ParentActor ${self.path} received message...")
      childActor ! "Message"
  }
}

object ChildActorA extends NamedActor{

  override final val name = "ChildActorA"
  def props() = Props(classOf[ChildActorA])

}

class ChildActorA extends Actor {

  def receive = {
    case "Message" =>
      println("ChildActorA received message...")
  }
}

object ChildActorB extends NamedActor{

  override final val name = "ChildActorB"
  def props() = Props(classOf[ChildActorB])

}


class ChildActorB extends Actor {

  def receive = {
    case "Message" =>
      println("ChildActorB received message...")
  }
}

来自sbt的输出

> run
[info] Running Bootstrap 
ParentActor akka://actor-system/user/$b received message...
ParentActor akka://actor-system/user/$d received message...
ChildActorB received message...
ChildActorA received message...
[success] Total time: 1 s, completed Jun 14, 2016 1:23:59 AM

你必须明确地命名孩子,

这不是最纯粹或最优雅的答案,我确信代码可以优化,但它允许您创建具有不同子代的同一父代的实例 .

我在想你也可以使用BindingAnnotations