通过手动调用构造函数来创建props对象是否安全并且建议这样做?

13秒

我试图与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
}

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

马特乌斯·库布佐克(Mateusz Kubuszok)

如果您知道JVM的工作方式,这将更容易理解。如果使用classOf[ValueClassActor]和参数列表实例化对象,则JVM必须ConstructorClass对象中提取对象,然后使用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实际看到的构造函数,则需要传递Stringnottead 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这是解释的捷径,因为开箱即用的objects是可序列化的,您必须对它们进行奇怪的处理才能使其不可序列化。例如,如果您做了

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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在 MySQL 数据库中手动创建行而不是通过 Hibernate 这样做是否安全?

如何通过手动函数将所有变量包含到回归中?

从普通对象创建类实例,而无需手动构造函数

从QThread派生的类的析构函数中手动调用终止()和退出()是否安全?

Git通过手动合并更新本地文件

OnPropertyChanged() 仅通过手动编辑 TextBox 触发

创建并返回在函数中创建的对象是否安全?

检查对象是否存在,如果不存在则手动引发错误

通过显式调用其析构函数和构造函数来重新创建对象

手动调用OpenGL函数

手动调用析构函数

手动调用jquery mouseover函数

Scheme中的手动函数调用

在C ++中手动调用构造函数时,幕后会发生什么?

调用基类构造函数而无需手动提供参数

如何通过手动限制设置来协调Matplotlib散点图中的点注释?

通过手动编辑packages.config安装多个nuget软件包

通过手动删除所有文件来中断Okular

通过手动定义的联接表按关联顺序查找

通过手动输入的ID在JSON数组中查找

如何通过手动设置缩放级别在地图上适合屏幕?

Laravel CRON 只能通过手动命令工作,而不是每分钟触发命令

如何通过手动单击或提交消息来触发作业

如何通过手动设置的网桥使用VPN(主要接口不受NetworkManager的管理)?

需要增加价值才能通过手动数据填充下拉菜单

计数器不起作用,但通过手动输入起作用

将延迟的dll加载与LoadLibraryA的手动调用混合使用是否安全?

如何使用 jsPsych 心理物理学插件中的“手动”属性调用函数来绘制刺激?

构造函数返回对象是否正确?