是否通过手动安全地建议使用构造函数来创建道具对象,并建议使用?

问题描述 投票:1回答:1

我正试图让Akka 演员成为家常便饭,但我无法解决这两个问题:首先,如所解释的here闭包可能导致序列化问题。下面的示例包含一个Props对象,该对象不可序列化,因为它关闭了不可序列化的对象:

case class Helper(name: String)

object MyNonserializableObject {

   val helper = Helper("the helper")

   val props7 = Props(new MyActor(helper))
}

因此,建议不要创建这样的Actor。上面的答案与Akka文档dangerous variants有关。另一方面,当我们处理值类作为构造函数参数 Akka docs recommends通过手动调用构造函数来创建道具时,以下代码上的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
}

这两个概念对我来说很矛盾。顺便说一下,由于我的排名,我无法将此问题创建为评论。

scala akka-actor
1个回答
1
投票

如果您知道JVM的工作原理,这将更容易理解。如果使用classOf[ValueClassActor]和args列表实例化对象,则JVM必须从Constructor对象中提取Class,然后使用Java Reflection 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类型仅在编译时存在(好吧,大多数情况下是sometimes Scala instantiates it),如果要调用JVM实际看到的构造函数,则需要传递StringArgument isadad。这就是为什么您具有这种行为的原因:

  def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
  def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok

为了避免处理此问题,您可以使用不依赖于运行时反射的Props creator

Props

危险吗?文档说:

CAVEAT:在创建实例时使用匿名mixin组合时,无法检测到所需的邮箱类型。例如,以下内容将不会检测到Stash中定义的DequeBasedMessageQueueSemantics需求:

def apply[T <: Actor](creator: => T)(implicit arg0: ClassTag[T]): Props

相反,您必须创建一个混合了特征的命名类,例如MyActor类使用Stash扩展Actor。

这意味着,只要您仅使用命名类,并为其提供参数,而在匿名子类上不添加任何minxin,就可以消除一个潜在的问题。为了避免出现关闭问题,您可以执行'Props(new Actor with Stash { ... })并在伴随对象中创建exactly said in documentation构造。

问题是,当您尝试创建Prop时,如果通过Internet将其发送到应用程序的另一部分,则可能会被序列化。 Akka群集。而且,如果您尝试序列化一个函数(在这里:匿名Prop,它是新的ValueClassActor(arg)),那么如果您尝试对其进行序列化,它将获取整个闭包。由于Java的工作方式,该函数将具有一个指向创建它的父对象的指针。

如果有

Function

然后看一下生成的字节码,就会发现有

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 } 并创建lambda Foo.hello时,该函数的关闭仅是伴随对象实例
  • 同伴对象正在实现new Foo("test")
  • 因此满足函数闭包可序列化的要求

Serializable是一种捷径,因为它是开箱即用的object MyNonserializableObject s are可序列化的,您必须对它们做一些奇怪的事情才能使其不可序列化。例如。如果您做了

object

closure将保留对trait Bar { object Baz { def hello: Foo = new Foo("test") // "test" is by-name so it has closure } } 的引用,而该引用将保留对Baz的引用,并且如果扩展Bar的任何内容均不可序列化,则不会是该closure。但是,如果您将在顶级Bar中生成lambda(未嵌套在其他类中,等等),则您的闭包只能依赖于可序列化的内容(因为object本身具有空的构造函数并实现object接口),因此可以自行序列化。

Serializable和命名参数相同的原理起作用。如果在顶级(或保证保证可序列化)的伴随对象中使用名称参数创建Props,则闭包也将可序列化并且使用将是安全的。就像文档建议中所述。

长话短说:

Prop

很安全。

© www.soinside.com 2019 - 2024. All rights reserved.