【问题标题】:How can Serializable.class not be assignable from Class.class?Serializable.class 怎么不能从 Class.class 分配?
【发布时间】:2020-07-24 06:13:32
【问题描述】:

org.springframework.core.SerializableTypeWrapper(版本5.2.3)中,第112行有如下代码:

    if (GraalDetector.inImageCode() || !Serializable.class.isAssignableFrom(Class.class)) {
        // Let's skip any wrapping attempts if types are generally not serializable in
        // the current runtime environment (even java.lang.Class itself, e.g. on Graal)
        return providedType;
    }

我对第二次检查 (!Serializable.class.isAssignableFrom(Class.class)) 感到好奇:它是否可以评估为 true(也就是说,Serialazable.class 不能从 Class.class 分配)?

Class#isAssignableFrom()javadoc 是这样说的:

确定此 Class 对象表示的类或接口是否与指定的 Class 参数表示的类或接口相同,或者是其超类或超接口。

查看Class的代码,看到如下:

public final class Class<T> implements java.io.Serializable

所以SerializableClass 的超接口并且应该总是 可以从Class 分配。但 Spring 代码中的检查表明有时不是。

怎么会?在什么情况下会发生这种情况?为什么它们不违反 Java 语言规范?

【问题讨论】:

  • if 块内的代码注释看起来很相关:“如果类型在当前运行时环境中通常不可序列化(甚至 java.lang.Class 本身,例如在 Graal 上)”。我认为“Graal”是指GraalVM,在创建本机图像时有limitations。我猜在 GraalVM 上可能是 Class 实现 Serializable?他们用它来测试当前环境是否是 Graal。但我不确定。
  • @Slaw 我已经编译了一个带有以下行的测试程序System.out.println("Is assignable: " + java.io.Serializable.class.isAssignableFrom(Class.class));;它在使用 java(OpenJDK 和 GraalVM 版本)运行时或当我使用 GraalVM 的 native-image 工具从该程序构建本机映像时都会输出 true。所以(至少在当前版本的 Graal 中),这在原生图像中似乎是不可能的(除非有一些特殊条件......)。

标签: java spring graalvm graalvm-native-image


【解决方案1】:

自定义class loader 是表达式返回false 的一种可能(如果不太可能)机制。自定义类加载器可以做一些疯狂的事情,包括加载他们自己版本的标准 Java 类。关于类加载器的一些知识:

  • 自定义类加载器可以配置为加载任何类,甚至包括 Java 标准库类(当然不鼓励这样做,但仍然可以)
  • 自定义类加载器通常配置为将不知道如何加载的类委托给系统类加载器。
  • 当 A 类引用 B 类时,将使用用于加载 A 类的任何类加载器来解析该引用
  • 可以配置多个类加载器来处理同一个类,这可能会导致将一个类的多个版本加载到 JVM 中,您获得的实际实现取决于您询问的类加载器

假设有一个自定义类加载器,无论出于何种原因,它被配置为自行加载java.io.Serializable,但委托给系统类加载器以加载其他类,包括java.lang.Class

现在假设这个自定义类加载器用于加载SerializableTypeWrapper。这意味着它还将用于解析SerializableTypeWrapper 中对java.io.Serializable 的引用。通过引用java.lang.Class,自定义类加载器会将其委托给系统类加载器。系统类加载器将用于加载java.lang.Class,但它也将用于从java.lang.Class 中加载对java.io.Serializable 的引用。

所以现在我们可以问这个问题 - java.io.Serializable [custom] 可以从 java.lang.Class [standard] 分配吗?答案是否定的——java.lang.Class 确实实现了java.io.Serializable [standard],但它没有实现java.io.Serializable [custom]

【讨论】:

  • 感谢您的解释。我想知道Class.class instanceof Serializable 在您的示例中会产生什么。它可能仍然是true,因为右侧的表达式不是类实例(对于不同的类加载器可能不同)而是类名(在运行时,可能是常量池的成员),对吗?
  • RHS 必须是引用类型,而不是简单的类名字符串。尽管在常量池中查找它是对的。但是,常量池是每个类的,并且使用加载类的类加载器执行查找。所以这将是相同的结果 - 如果类加载器已知的 SerializableClass 实现的 Serializable 不同,它将返回 false
  • JVM 规范有一些很好的细节,如果你有兴趣:docs.oracle.com/javase/specs/jvms/se8/html/… 另外,关于类解析:docs.oracle.com/javase/specs/jvms/se8/html/…
猜你喜欢
  • 2013-06-27
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 2012-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多