我试图与Akka演员见面,而我却无法解决这两个问题:首先,如此处所述,闭包可能会导致序列化问题。下面的示例包含一个Props对象,该对象不可序列化,因为它关闭了不可序列化的对象:
case class Helper(name: String)
object MyNonserializableObject {
val helper = Helper("the helper")
val props7 = Props(new MyActor(helper))
}
因此建议不要创建这样的Actor。上面的答案与Akka docs危险变体有关。另一方面,当我们将值类作为构造函数参数处理时,Akka docs建议通过手动调用构造函数来创建道具,props3
以下代码是以下示例:
class Argument(val value: String) extends AnyVal
class ValueClassActor(arg: Argument) extends Actor {
def receive = { case _ => () }
}
object ValueClassActor {
def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok
def props3(arg: Argument) = Props(new ValueClassActor(arg)) // ok
}
这两个概念对我来说似乎很矛盾。顺便说一句,由于我的排名,我无法将此问题创建为评论。
如果您知道JVM的工作方式,这将更容易理解。如果使用classOf[ValueClassActor]
和参数列表实例化对象,则JVM必须Constructor
从Class
对象中提取对象,然后使用Java反射API实例化对象。
同时,如果您看一下什么AnyVal
是,您将看到该课程AnyVal
class Argument(val value: String) extends AnyVal
class ValueClassActor(arg: Argument)
编译为:
Compiled from "test.scala"
public class ValueClassActor {
public ValueClassActor(java.lang.String);
Code:
0: aload_0
1: invokespecial #14 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LValueClassActor;
0 5 1 arg Ljava/lang/String;
}
因此,Argument
类型仅在编译时存在(通常,Scala有时会实例化它),并且如果要调用JVM实际看到的构造函数,则需要传递String
nottead of Argument
。这就是为什么您具有以下行为的原因:
def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok
为了避免处理这个问题,你可以使用Props
创作者不依赖于运行时反射:
def apply[T <: Actor](creator: => T)(implicit arg0: ClassTag[T]): Props
危险吗?文档说:
CAVEAT:在创建实例时使用匿名混合组合无法检测到所需的邮箱类型。例如,以下代码不会检测到对Stash中定义的DequeBasedMessageQueueSemantics的需求:
'Props(new Actor with Stash { ... })
相反,您必须创建一个混合了特征的命名类,例如,类MyActor用Stash扩展Actor。
这意味着,只要您仅使用命名类并仅提供名称类,而没有匿名子类上的任何minxin,就可以消除一个潜在的问题。为了避免出现关闭问题,您可以按照文档中的确切说明进行操作,并Prop
在伴随对象中创建该构造。
问题是,当您尝试创建时Prop
,如果通过Internet将其发送到应用程序的另一部分(如果有Akka群集),则可以对其进行序列化。而且,如果您尝试序列化一个函数(此处为:匿名类Function
,即“新ValueClassActor(arg))”,那么如果您尝试对其进行序列化,它将获取整个闭包。由于Java的工作方式,该函数将具有一个指向创建它的父对象的指针。
如果你有
class Foo(s: => String)
object Foo {
def hello: Foo = new Foo("test") // "test" is by-name so it has closure
}
看一下生成的字节码,您会发现有
Compiled from "foo.scala"
public class Foo {
public static Foo hello();
Code:
0: getstatic #16 // Field Foo$.MODULE$:LFoo$;
3: invokevirtual #18 // Method Foo$.hello:()LFoo;
6: areturn
public Foo(scala.Function0<java.lang.String>);
Code:
0: aload_0
1: invokespecial #25 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
line 1: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LFoo;
0 5 1 s Lscala/Function0;
}
和
Compiled from "foo.scala"
public final class Foo$ {
public static final Foo$ MODULE$;
public static {};
Code:
0: new #2 // class Foo$
3: dup
4: invokespecial #17 // Method "<init>":()V
7: putstatic #19 // Field MODULE$:LFoo$;
10: return
LineNumberTable:
line 3: 0
public Foo hello();
Code:
0: new #23 // class Foo
3: dup
4: invokedynamic #44, 0 // InvokeDynamic #0:apply:()Lscala/Function0;
9: invokespecial #47 // Method Foo."<init>":(Lscala/Function0;)V
12: areturn
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this LFoo$;
public static final java.lang.String $anonfun$hello$1();
Code:
0: ldc #50 // String test
2: areturn
LineNumberTable:
line 4: 0
}
意思就是:
Foo.hello
并创建lambda时new Foo("test")
,该函数的关闭仅是伴随对象实例Serializable
object MyNonserializableObject
这是解释的捷径,因为开箱即用的object
s是可序列化的,您必须对它们进行奇怪的处理才能使其不可序列化。例如,如果您做了
trait Bar {
object Baz {
def hello: Foo = new Foo("test") // "test" is by-name so it has closure
}
}
闭包将保留对的引用Baz
,而闭包将保留对的引用Bar
,如果扩展Bar
不能序列化,则闭包也不会。但是,如果您将在object
顶级(而不是嵌套在其他类等中)内部生成lambda ,则您的关闭操作只能依赖于可序列化的内容(因为object
它们本身具有空的构造函数和实现Serializable
接口) ,因此可以自行序列化。
对于Props
参数和名称参数,相同的原理也适用。如果Prop
在顶级(或保证可序列化)的伴随对象中创建using by-name参数,则闭包也将可序列化,并且使用将是安全的。就像doc推荐说的那样。
长话短说:
class ValueClassActor(arg: Argument) extends Actor {
def receive = { case _ => () }
}
object ValueClassActor {
def props(arg: Argument) = Props(new ValueClassActor(arg))
}
是安全的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句