首页 文章

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

提问于
浏览
2

我希望能够创建同一父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 回答

  • 3

    我正在使用一些基于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

相关问题