简体   繁体   English

如何根据类名创建一个Akka Actor

[英]How to create an Akka Actor given the class name

I would like to create an Akka actor by using the class name, as shown. 我想通过使用类名创建一个Akka actor,如图所示。 I've tried many variations of system.actorOf(new Props(theProcessor.getClass), name = "Test"), but I just cannot get this to work. 我已经尝试过system.actorOf(new Props(theProcessor.getClass),name =“Test”)的许多变体,但我无法让它工作。 Any idea in creating an Actor from the class loader 从类加载器创建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

You could use the Java API (just for the Props instantiation): 您可以使用Java API(仅用于Props实例化):

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

The important part is the new keyword. 重要的部分是new关键字。 If you ommit it, you will use the Scala API instead, which does not allow instantiation by class. 如果省略它,则将使用Scala API,而不允许按类实例化。

Sounds like you want to use the same ClassLoader as the one that is used by your ActorSystem, so this is easily solved with a custom Akka Extension (Warning, not actually compiled but you'll get the gist of it) 听起来你想使用与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"))

(As indicated in the stacktrace) you have to use either (如堆栈跟踪中所示)您必须使用其中之一

  • system.actorOf(Props[TestProcessor]) (no new keyword, you pass a type) system.actorOf(Props[TestProcessor]) (没有new关键字,你传递一个类型)
  • or system.actorOf(Props(new TestProcessor())) ( new keyword, you pass an instance) system.actorOf(Props(new TestProcessor()))new关键字,你传递一个实例)

If you can get a Props from a String then you can get an actor from a String. 如果你可以从String中获取Props,那么你可以从String中获取一个actor。 Here is how to get a Props from a String: 以下是如何从字符串中获取道具:

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

I put this up because none of the other answers use Props.apply - a relatively recent enhancement to Akka. 我把它放了,因为没有其他答案使用Props.apply - 对Akka的一个相对较新的增强。 It is easy to pass in constructor arguments, for instance the current actorRef, for example: 很容易传递构造函数参数,例如当前的actorRef,例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM