>我开发了一个存储来自设备的数据的小应用程序:我选择以 JSON 格式存储数据,数据的序列化/反序列化工作得很好,即使它涉及我创建的一些自定义类型......但只有我在 IDE(Eclipse,就此而言(工作。
但是,当我导出可运行的JAR文件时,数据的反序列化会遇到某种问题,因为软件总是抛出以下异常:
Caused by: java.lang.UnsupportedOperationException: Cannot allocate class LocalDateTime
at com.google.gson.internal.UnsafeAllocator$4.newInstance(UnsafeAllocator.java:104)
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
... 88 common frames omitted
我以为我会遇到自定义类型的问题,而不是内置类型。在这一点上,我发现了两件事:
- 如果我使用完整的 JRE 9 来运行 JAR 文件,则不会引发异常:我仔细检查了使用 Jlink.exe 创建的自定义 JRE 中包含的模块,并且所有内容都正确包含。我仍然想使用较小的JRE,所以我还没有进一步调查(我想这解释了为什么在IDE中它可以完美运行(
- 我向 Gson 对象添加了一个自定义反序列化程序(见下文(,我只需手动将 JSON 字符串转换为有效数据,从而避免了 LocalDateTime 类的异常......但例外再次出现在另一个班级上,这次是定制的。
在这一点上,我想我可以简单地为导致问题的每种数据类型添加一个反序列化程序,但我想知道为什么完整的 JRE 不会发生问题,以及为什么较小的 JRE 会导致这种情况,即使包含所有所需的模块。也许还值得一提的是,我没有向保存数据的 Gson 对象添加自定义序列化程序,它都是按照 Gson 默认值序列化的。
LocalDateTime
解串器:
@Override
public LocalDateTime deserialize(JsonElement json, java.lang.reflect.Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject joDate = json.getAsJsonObject().get("date").getAsJsonObject();
JsonObject joTime = json.getAsJsonObject().get("time").getAsJsonObject();
//JSON example: {"date":{"year":2019,"month":1,"day":9},"time":{"hour":6,"minute":14,"second":1,"nano":0}
return LocalDateTime.of(joDate.get("year").getAsInt(),
joDate.get("month").getAsInt(),
joDate.get("day").getAsInt(),
joTime.get("hour").getAsInt(),
joTime.get("minute").getAsInt(),
joTime.get("second").getAsInt(),
joTime.get("nano").getAsInt());
}
}
Jdeps.deps 模块列表:
com.google.gson
java.base
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
org.slf4j
在我收到答案后,我在这里打开了一个问题。
TL;博士
你需要一个包含模块jdk.unsupport的运行时映像(例如完整的 JDK 或使用 jlink 构建的东西(。
完整答案
GSON 希望在不调用任何构造函数的情况下创建它反序列化的类的实例(因此,如果没有 GSON 这么说,就不会初始化任何内容(。这通常无法完成,但sun.misc.Unsafe
提供了一种使用方法allocateInstance
执行此操作的方法。为此,GSON 需要一个实例sun.misc.Unsafe
.调用堆栈中最上面的帧来自UnsafeAllocator
,它使用常见的技巧来获取Unsafe
。
问题是,sun.misc.Unsafe
在模块jdk.unsupport中,它存在于完整的 JDK 中,但您通常不会在运行时映像中找到。
使用 jlink 创建运行时映像时,请确保包含选项--add-modules jdk.unsupported
,您应该很高兴。
可以说,GSON 应该声明对jdrequires static
k的可选依赖
我在打包compose
桌面应用程序时遇到了同样的问题。
更新build.gradle
文件,添加unsupported
模块。
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "admin"
packageVersion = "1.0.0"
modules("java.sql")
modules("jdk.unsupported")
}
}
}