Skip to content

Commit 2d4de3c

Browse files
committed
refactor(codegen): 替换Java原生序列化为Jackson JSON序列化
- 使用Jackson JSON序列化替代ObjectOutputStream/ObjectInputStream - 避免JDK版本间serialVersionUID不兼容问题 - 在序列化格式中嵌入类名以支持准确的类型重建 - 添加Base64编码的JSON字符串序列化功能 - 实现从Base64编码数据反序列化对象的方法 - 更新原生镜像配置以包含必要的构造函数反射支持
1 parent cd8c793 commit 2d4de3c

2 files changed

Lines changed: 62 additions & 10 deletions

File tree

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,70 @@
11
package dev.flexmodel.codegen;
22

3-
import java.io.*;
3+
import dev.flexmodel.JsonUtils;
4+
5+
import java.io.IOException;
6+
import java.io.ObjectInputStream;
7+
import java.io.ObjectOutputStream;
8+
import java.nio.charset.StandardCharsets;
49
import java.util.Base64;
510

611
/**
12+
* Object serialization utilities.
13+
* <p>
14+
* Uses Jackson JSON serialization (wrapped in Base64) instead of Java native
15+
* serialization ({@link ObjectOutputStream}/{@link ObjectInputStream}) to avoid
16+
* {@code serialVersionUID} incompatibility between JDK versions. JDK classes
17+
* like {@link java.util.HashMap} and {@link java.util.ArrayList} change their
18+
* {@code serialVersionUID} across releases, which breaks deserialization when
19+
* the codegen JDK differs from the runtime JDK (e.g., in GraalVM native-image).
20+
* </p>
21+
*
722
* @author cjbi
823
*/
924
public class ObjectUtils {
1025

26+
private ObjectUtils() {
27+
}
28+
29+
/**
30+
* Serialize an object to a Base64-encoded JSON string.
31+
* <p>
32+
* The serialized format embeds the class name as the first line, followed by
33+
* the JSON representation. This allows deserialization to reconstruct the
34+
* exact object type without requiring an explicit type parameter.
35+
* </p>
36+
*/
1137
public static String serialize(Object obj) throws IOException {
12-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
13-
ObjectOutputStream oos = new ObjectOutputStream(baos);
14-
oos.writeObject(obj);
15-
oos.flush();
16-
return Base64.getEncoder().encodeToString(baos.toByteArray());
38+
String json = JsonUtils.toJsonString(obj);
39+
String className = obj.getClass().getName();
40+
String combined = className + "\n" + json;
41+
return Base64.getEncoder().encodeToString(combined.getBytes(StandardCharsets.UTF_8));
1742
}
1843

44+
/**
45+
* Deserialize a Base64-encoded JSON string back to an object.
46+
* <p>
47+
* The first line of the decoded data is the fully qualified class name,
48+
* which is used to determine the target type for Jackson deserialization.
49+
* </p>
50+
*
51+
* @param str Base64-encoded serialized data
52+
* @return the deserialized object
53+
* @throws IOException if I/O error occurs
54+
* @throws ClassNotFoundException if the target class cannot be found
55+
*/
1956
public static Object deserialize(String str) throws IOException, ClassNotFoundException {
2057
byte[] data = Base64.getDecoder().decode(str);
21-
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
22-
return ois.readObject();
58+
String combined = new String(data, StandardCharsets.UTF_8);
59+
int newlineIdx = combined.indexOf('\n');
60+
if (newlineIdx < 0) {
61+
throw new IOException("Invalid serialized format: missing class name header");
62+
}
63+
String className = combined.substring(0, newlineIdx);
64+
String json = combined.substring(newlineIdx + 1);
65+
@SuppressWarnings("unchecked")
66+
Class<?> clazz = Class.forName(className);
67+
return JsonUtils.parseToObject(json, clazz);
2368
}
2469

2570
}

‎flexmodel-server/src/main/java/dev/flexmodel/common/config/nativeimage/FlexmodelNativeProcessor.java‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ ReflectiveClassBuildItem registerDtos() {
107107

108108
/**
109109
* 注册引擎模块中的模型定义类。
110-
* 这些类使用 Jackson 多态反序列化(@JsonSubTypes / @JsonTypeInfo)。
110+
* 这些类使用 Jackson 多态反序列化(@JsonSubTypes / @JsonTypeInfo),
111+
* 并通过 ObjectUtils.deserialize() 在运行时从 JSON 反序列化。
112+
* <p>
113+
* <b>注意</b>:必须包含 {@code .constructors()},因为 Jackson 在原生镜像中
114+
* 需要通过反射调用无参构造函数来实例化模型对象。
111115
*/
112116
@BuildStep
113117
ReflectiveClassBuildItem registerModelDefinitions() {
@@ -116,8 +120,11 @@ ReflectiveClassBuildItem registerModelDefinitions() {
116120
"dev.flexmodel.model.field.**",
117121
"dev.flexmodel.condition.**",
118122
"dev.flexmodel.event.**",
119-
"dev.flexmodel.event.impl.**"
123+
"dev.flexmodel.event.impl.**",
124+
"dev.flexmodel.ModelImportBundle",
125+
"dev.flexmodel.ModelImportBundle$ImportData"
120126
)
127+
.constructors()
121128
.methods()
122129
.fields()
123130
.build();

0 commit comments

Comments
 (0)