【问题标题】:How to create an Akka Actor given the class name如何在给定类名的情况下创建 Akka Actor
【发布时间】:2013-03-18 12:01:06
【问题描述】:

我想使用类名创建一个 Akka 演员,如图所示。 我尝试了许多 system.actorOf(new Props(theProcessor.getClass), name = "Test"), 但我就是无法让它发挥作用。从类加载器创建 Actor 的任何想法

package com.test

import akka.actor.{Props, Actor, ActorRef, ActorSystem}

object Main {
  def main(args: Array[String]) {
   ActorFromString("Test")
  }
}

object ActorFromString {
  implicit val system = ActorSystem("Test")
  def apply(name: String): ActorRef = {
    val className = "com.test." + name + "Processor"
    val theProcessor: Actor = Class.forName(className).newInstance().asInstanceOf[Actor]
    system.actorOf(new Props(theProcessor.getClass), name = "Test")
  }
}

class TestProcessor extends Actor {
  def receive = {
    case data => println("processing data")
  }
}

Exception in thread "main" akka.actor.ActorInitializationException:
You cannot create an instance of [com.test.TestProcessor] explicitly using the constructor (new).
You have to use one of the factory methods to create a new actor. Either use:
'val actor = context.actorOf(Props[MyActor])'        (to create a supervised child actor from    within an actor), or
'val actor = system.actorOf(Props(new MyActor(..)))' (to create a top level actor from the   ActorSystem)
at akka.actor.ActorInitializationException$.apply(Actor.scala:166)
at akka.actor.Actor$class.$init$(Actor.scala:377)
at com.test.TestProcessor.<init>(ActorFromString.scala:20)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at  sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at java.lang.Class.newInstance0(Class.java:374)
at java.lang.Class.newInstance(Class.java:327)
at com.test.ActorFromString$.apply(ActorFromString.scala:15)
at com.test.Main$.main(ActorFromString.scala:7)
at com.test.Main.main(ActorFromString.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Process finished with exit code 143

【问题讨论】:

  • 我意识到我的问题可能不是很清楚。这里的关键是传入一个名称并从中构造一个actor。该名称不是提前知道的,而是来自一组可能的类名。这里的测试示例只有一个类“TestProcessor”,可能还有另一个类“Test2Processor”,等等......
  • 我想我已经解决了。我需要在道具里面构造演员 thing.system.actorOf(Props(Class.forName(className).newInstance().asInstanceOf[Actor]), name = device)

标签: scala akka


【解决方案1】:

(如堆栈跟踪所示)您必须使用任一

  • system.actorOf(Props[TestProcessor])(没有new关键字,你传一个类型)
  • 或system.actorOf(Props(new TestProcessor()))(new关键字,你传递一个实例)

【讨论】:

  • 为了挑剔,你不传递一个实例,你传递一个将在每次需要新的actor实例时执行的按名称参数。
  • @EndreVarga 是真的。应用方法签名是def apply(creator: ⇒ Actor): Props = default.withCreator(creator)。参数是一个 thunk,它创建一个 actor 的实例。
【解决方案2】:

您可以使用 Java API(仅用于 Props 实例化):

val myActor = system.actorOf( 
  new Props( Class.forName( "myActorClassName" ).asInstanceOf[Class[Actor]] ) 
)

重要的部分是new 关键字。如果您省略它,您将使用 Scala API 代替,它不允许按类实例化。

【讨论】:

    【解决方案3】:

    听起来您想使用与您的 ActorSystem 使用的 ClassLoader 相同的 ClassLoader, 所以这很容易通过自定义 Akka 扩展解决(警告,实际上并未编译,但你会明白它的要点)

    import akka.actor.{ Extension, ExtensionId, ExtensionIdProvider, ExtendedActorSystem, DynamicAccess }
    // This will be instantiated once per ActorSystem instance
    class Reflection(access: DynamicAccess) extends Extension {
      // Loads the Class with the provided Fully Qualified Class Name using the given DynamicAccess
      // Throws exception if it fails to load
      def actorClassFor(fqcn: String) = access.getClassFor[Actor](fqcn).get
    }
    // This is how we access the Reflection extension, you can view it as an ActorSystemLocal
    object Reflect extends ExtensionId[Reflection] with ExtensionIdProvider {
      override def lookup = Reflect
      override def createExtension(system: ExtendedActorSystem) = new Reflection(system.dynamicAccess)
    }
    // Load the extension if not already loaded, return the instance for this ActorSystem and call the actorClassFor
    system.actorOf(Props(Reflect(system).actorClassFor("com.test." + name + "Processor"))
    

    【讨论】:

      【解决方案4】:

      如果您可以从字符串中获取道具,那么您可以从字符串中获取演员。以下是如何从字符串中获取道具:

      Props.apply(Class.forName( "myActorClassName" ).asInstanceOf[Class[Actor]])
      

      我提出这个是因为其他答案都没有使用 Props.apply - Akka 的一个相对较新的增强。很容易传入构造函数参数,例如当前的actorRef,例如:

      def selfActorRef = context.self
      val gateKeeperRef = context.actorOf(
        Props.apply(Class.forName(findGateKeeperName).asInstanceOf[Class[Actor]],
        selfActorRef)
      )
      

      【讨论】:

        猜你喜欢
        • 2013-06-15
        • 2015-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-17
        • 2011-05-04
        • 1970-01-01
        • 2011-03-30
        相关资源
        最近更新 更多