字段配置
本页说明如何在 Java 中配置序列化字段级元信息。
概述
Apache ForyTM 通过注解提供字段级配置:
@ForyField:配置字段元信息(id、nullable、ref、dynamic)@Ignore:将字段排除在序列化之外- 整数类型注解:控制整数编码方式(varint、fixed、tagged、unsigned)
这些能力可用于:
- Tag ID:为兼容模式分配紧凑数值 ID,降低 struct 字段元信息开销
- 可空控制:声明字段是否允许为 null
- 引用跟踪:为共享对象开启引用跟踪
- 字段跳过:显式排除不需要序列化的字段
- 编码控制:指定整数序列化编码策略
- 多态控制:控制 struct 字段是否写入运行时类型信息
基本语法
在字段上使用注解:
import org.apache.fory.annotation.ForyField;
public class Person {
@ForyField(id = 0)
private String name;
@ForyField(id = 1)
private int age;
@ForyField(id = 2, nullable = true)
private String nickname;
}
@ForyField 注解
使用 @ForyField 配置字段级元信息:
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@ForyField(id = 2, nullable = true)
private String email;
@ForyField(id = 3, ref = true)
private List<User> friends;
@ForyField(id = 4, dynamic = ForyField.Dynamic.TRUE)
private Object data;
}
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id | int | -1 | 字段 tag ID(-1 表示使用字段名编码) |
nullable | boolean | false | 字段是否可为 null |
ref | boolean | false | 是否开启引用跟踪 |
dynamic | Dynamic | AUTO | 控制 struct 字段多态行为 |
字段 ID(id)
通过为字段分配数值 ID,可降低兼容模式下 struct 字段元信息开销:
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@ForyField(id = 2)
private int age;
}
收益:
- 序列化体积更小(元信息里用数值 ID 而不是字段名)
- struct 字段元信息开销更低
- 可在不破坏二进制兼容性的前提下重命名字段
建议: 兼容模式下建议配置字段 ID,以降低序列化成本。
注意:
- 同一个类内 ID 必须唯一
- ID 必须
>= 0(-1表示使用字段名编码,也是默认行为) - 未指定时,元信息将写字段名(开销更大)
不配置字段 ID(元信息使用字段名)示例:
public class User {
private long id;
private String name;
}
可空字段(nullable)
对可能为 null 的字段使用 nullable = true:
public class Record {
// 可空字符串字段
@ForyField(id = 0, nullable = true)
private String optionalName;
// 可空 Integer 字段(装箱类型)
@ForyField(id = 1, nullable = true)
private Integer optionalCount;
// 非可空字段(默认)
@ForyField(id = 2)
private String requiredName;
}
注意:
- 默认是
nullable = false(不可空) nullable = false时,Fory 会省略 null 标记写入(节省 1 字节)- 可能为 null 的装箱类型(
Integer、Long等)建议显式设为nullable = true
引用跟踪(ref)
对于可能共享或循环引用的字段,启用引用跟 踪:
public class RefOuter {
// 两个字段可能指向同一个内部对象
@ForyField(id = 0, ref = true, nullable = true)
private RefInner inner1;
@ForyField(id = 1, ref = true, nullable = true)
private RefInner inner2;
}
public class CircularRef {
@ForyField(id = 0)
private String name;
// 自引用字段,用于循环引用
@ForyField(id = 1, ref = true, nullable = true)
private CircularRef selfRef;
}
适用场景:
- 字段可能形成循环或共享关系
- 同一对象被多个字段引用
注意:
- 默认是
ref = false(不跟踪引用) ref = false可避免 IdentityMap 开销,也会跳过引用跟踪标记- 仅在全局启用 ref tracking 时,字段级 ref 才生效
Dynamic(多态控制)
控制跨语言序列化时 struct 字段的多态行为:
public class Container {
// AUTO:接口/抽象类型动态,具体类型非动态
@ForyField(id = 0, dynamic = ForyField.Dynamic.AUTO)
private Animal animal; // 接口类型,写入类型信息
// FALSE:不写类型信息,直接按声明类型序列化
@ForyField(id = 1, dynamic = ForyField.Dynamic.FALSE)
private Dog dog; // 具体类型,不写类型信息
// TRUE:写类型信息,支持运行时子类型
@ForyField(id = 2, dynamic = ForyField.Dynamic.TRUE)
private Object data; // 强制多态
}
取值:
| 值 | 说明 |
|---|---|
AUTO | 自动判断:接口/抽象类型动态,具体类型非动态 |
FALSE | 不写类型信息,直接使用声明类型的序列化器 |
TRUE | 写入类型信息,以支持运行时子类型 |
跳过字段
使用 @Ignore
将字段排除在序列化之外:
import org.apache.fory.annotation.Ignore;
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@Ignore
private String password; // 不序列化
@Ignore
private Object internalState; // 不序列化
}
使用 transient
Java 的 transient 关键字同样会排除字段:
public class User {
@ForyField(id = 0)
private long id;
private transient String password; // 不序列化
private transient Object cache; // 不序列化
}