Java 序列化
当只需要 Java 对象序列化时,该模式相比跨语言对象图序列化具有更优的性能。
快速开始
注意,Fory 实例的创建开销较大,Fory 实例应在多次序列化间复用,而不是每次都新建。 建议将 Fory 保存在静态全局变量、单例对象或有限数量的实例变量中。
单线程场景下 Fory 的用法:
import java.util.List;
import java.util.Arrays;
import org.apache.fory.*;
import org.apache.fory.config.*;
public class Example {
public static void main(String[] args) {
SomeClass object = new SomeClass();
// 注意:Fory 实例应在多次序列化间复用
Fory fory = Fory.builder().withLanguage(Language.JAVA)
.requireClassRegistration(true)
.build();
// 注册类型可减少类名序列化开销,但不是强制的。
// 启用类注册后,所有自定义类型都必须注册。
fory.register(SomeClass.class);
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}
多线程场景下 Fory 的用法:
import java.util.List;
import java.util.Arrays;
import org.apache.fory.*;
import org.apache.fory.config.*;
public class Example {
public static void main(String[] args) {
SomeClass object = new SomeClass();
// 注意:Fory 实例应在多次序列化间复用
ThreadSafeFory fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().withLanguage(Language.JAVA)
.withClassLoader(classLoader).build();
f.register(SomeClass.class);
return f;
});
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}
Fory 实例复用示例:
import java.util.List;
import java.util.Arrays;
import org.apache.fory.*;
import org.apache.fory.config.*;
public class Example {
// 复用 fory 实例
private static final ThreadSafeFory fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().withLanguage(Language.JAVA)
.withClassLoader(classLoader).build();
f.register(SomeClass.class);
return f;
});
public static void main(String[] args) {
SomeClass object = new SomeClass();
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}
ForyBuilder 配置选项
| 选项名 | 说明 | 默认值 |
|---|---|---|
timeRefIgnored | 是否忽略所有在 TimeSerializers 注册的时间类型及其子类的引用跟踪(当引用跟踪开启时)。如需对时间类型启用引用跟踪,可通过 Fory#registerSerializer(Class, Serializer) 注册。例如:fory.registerSerializer(Date.class, new DateSerializer(fory, true))。注意,启用引用跟踪需在包含时间字段的类型代码生成前完成,否则这些字段仍会跳过引用跟踪。 | true |
compressInt | 是否启用 int 压缩以减小序列化体积。 | true |
compressLong | 是否启用 long 压缩以减小序列化体积。 | true |
compressString | 是否启用字符串压缩以减小序列化体积。 | false |
classLoader | 类加载器不建议动态变更,Fory 会缓存类元数据。如需变更类加 载器,请使用 LoaderBinding 或 ThreadSafeFory。 | Thread.currentThread().getContextClassLoader() |
compatibleMode | 类型前向/后向兼容性配置。与 checkClassVersion 配置相关。SCHEMA_CONSISTENT:序列化端与反序列化端类结构需一致。COMPATIBLE:序列化端与反序列化端类结构可不同,可独立增删字段。详见。 | CompatibleMode.SCHEMA_CONSISTENT |
checkClassVersion | 是否校验类结构一致性。启用后,Fory 会写入并校验 classVersionHash。若启用 CompatibleMode#COMPATIBLE,此项会自动关闭。除非能确保类不会演化,否则不建议关闭。 | false |
checkJdkClassSerializable | 是否校验 java.* 下的类实现了 Serializable 接口。若未实现,Fory 会抛出 UnsupportedOperationException。 | true |
registerGuavaTypes | 是否预注册 Guava 类型(如 RegularImmutableMap/RegularImmutableList)。这些类型虽非公开 API,但较为稳定。 | true |
requireClassRegistration | 关闭后可反序列化未知类型,灵活性更高,但存在安全风险。 | true |
suppressClassRegistrationWarnings | 是否屏蔽类注册警告。警告可用于安全审计,但可能影响体验,默认开启屏蔽。 | true |
metaShareEnabled | 是否启用元数据共享模式。 | true(若设置了 CompatibleMode.Compatible,否则为 false) |
scopedMetaShareEnabled | 是否启用单次序列化范围内的元数据独享。该元数据仅在本次序列化中有效,不与其他序列化共享。 | true(若设置了 CompatibleMode.Compatible,否则为 false) |
metaCompressor | 设置元数据压缩器。默认使用基于 Deflater 的 DeflaterMetaCompressor,可自定义如 zstd 等更高压缩比的压缩器。需保证线程安全。 | DeflaterMetaCompressor |
deserializeNonexistentClass | 是否允许反序列化/跳过不存在的类的数据。 | true(若设置了 CompatibleMode.Compatible,否则为 false) |
codeGenEnabled | 是否启用代码生成。关闭后首次序列化更快,但后续序列化性能较低。 | true |
asyncCompilationEnabled | 是否启用异步编译。启用后,序列化先用解释模式,JIT 完成后切换为 JIT 模式。 | false |
scalaOptimizationEnabled | 是否启用 Scala 特定优化。 | false |
copyRef | 关闭后,深拷贝性能更好,但会忽略循环和共享引用。对象图中的同一引用会被拷贝为不同对象。 | true |
serializeEnumByName | 启用后,枚举按名称序列化,否则按 ordinal。 | false |
高级用法
Apache Fory™ 创建
单线程 Fory 示例:
Fory fory = Fory.builder()
.withLanguage(Language.JAVA)
// 启用共享/循环引用跟踪。若无重复引用可关闭以提升性能。
.withRefTracking(false)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
// 启用类型前向/后向兼容
// 若追求更小体积和更高性能可关闭
// .withCompatibleMode(CompatibleMode.COMPATIBLE)
// 启用异步多线程编译
.withAsyncCompilation(true)
.build();
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
线程安全 Fory 示例:
ThreadSafeFory fory = Fory.builder()
.withLanguage(Language.JAVA)
// 启用共享/循环引用跟踪。若无重复引用可关闭以提升性能。
.withRefTracking(false)
// 启用 int 压缩
// .withIntCompressed(true)
// 启用 long 压缩
// .withLongCompressed(true)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
// 启用类型前向/后向兼容
// 若追求更小体积和更高性能可关闭
// .withCompatibleMode(CompatibleMode.COMPATIBLE)
// 启用异步多线程编译
.withAsyncCompilation(true)
.buildThreadSafeFory();
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
序列化中的类结构演化
在实际系统中,序列化用到的类结构可能会随时间变化,比如字段的增删。当序列化和反序列化端使用不同版本的 jar 时,类结构可能不一致。
Fory 默认采用 CompatibleMode.SCHEMA_CONSISTENT,即要求序列化和反序列化端类结构一致,以获得最小的序列化体积和最高性能。如果结构不一致,反序列化会失败。
如需支持类结构演化(前向/后向兼容),需将 Fory 配置为 CompatibleMode.COMPATIBLE,允许字段增删,反序列化端可自动适配不同结构。
示例:
Fory fory = Fory.builder()
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.build();
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
兼容模式下,类元数据会写入序列化结果。Apache Fory™ 采用高效压缩算法降低元数据开销,但仍会有一定体积增加。为进一步降低元数据成本,Apache Fory™ 支持元数据共享机制,详情见Meta Sharing