本节摘要:options 机制允许在 proto 文件的任何层级(文件、message、字段、枚举等)挂载元数据:内置 options 如 deprecated、packed、json_name 开箱即用;自定义 options 通过扩展 google.protobuf.*Options 消息把领域元数据刻进描述符,再由插件或反射消费。本节走完自定义 option 的定义、赋值、读取全流程,并给出"什么元数据适合放 option"的设计判据。读完你应当能为团队建一套带语义标注的领域契约。
本章第一站:往罗塞塔石碑上刻新铭文。它承接第 4 章的描述符与插件——没有那两块地基,option 刻了也无人来读。
options 的语法形态是方括号里的键值对,挂在声明的分号之前:
message Artifact { string catalog_no = 1 [deprecated = true]; repeated int32 codes = 2 [packed = true]; // proto3 数值 repeated 默认已 packed string displayName = 3 [json_name = "display_name"]; // 定制 JSON 映射键名 }
三个内置项各代表一类用途:deprecated 是生命周期标注(生成代码产生编译警告,第 5.2 节删字段流程的第一步);packed 是编码行为开关;json_name 是跨格式映射定制。共同点:它们都是"契约本体之外的第二层信息"——不影响字段身份与编码格式,却指导工具与运行时的行为。这个观察就是自定义 option 的设计原点。
需求场景:团队想在字段上声明数据分级(public / internal / confidential),供文档生成、日志脱敏、CI 检查三个消费方使用。三步走:
第一步:定义 option 消息。 自定义 option 的本质是扩展 Protobuf 自描述体系里的 options 消息——每个可挂载层级都有一个对应的 Options 消息(字段级是 google.protobuf.FieldOptions),扩展它等于往石碑的固定位置加刻文:
syntax = "proto3"; package acme.annotations; import "google/protobuf/descriptor.proto"; message DataClass { // 先定义值的类型 enum Level { LEVEL_UNSPECIFIED = 0; LEVEL_PUBLIC = 1; LEVEL_INTERNAL = 2; LEVEL_CONFIDENTIAL = 3; } } extend google.protobuf.FieldOptions { DataClass data_class = 50001; // 扩展点:编号要用大号(自定义区间) }
第二步:赋值。 在业务契约里像内置 option 一样使用:
message Customer { string nickname = 1 [(acme.annotations.data_class).level = LEVEL_PUBLIC]; string phone = 2 [(acme.annotations.data_class).level = LEVEL_CONFIDENTIAL]; }
第三步:读取。 两条消费路径。编译期路径:第 4.3 节的文档插件里,f.GetOptions() 拿到字段 options 后,按扩展编号 50001 解出 DataClass——文档里 phone 字段旁边就能印上"机密"标记。运行时路径:反射 API 同样能读到(下一节展开)。两条路径共享同一份描述符数据,一次刻写、处处可读。
⚠️ 扩展编号的纪律:自定义 option 的编号要在描述符预留的大号区间(各语言实现普遍建议 50000 以上),且全工程唯一。两个团队都用 50001 定义不同的 option,合并描述符时就是编号重用事故的元数据版本。
option 很容易滥用,三条判据收口:
反例:把"这个字段每秒大约多少次更新"这类运营数据刻进 option——它是环境事实不是契约事实,属于监控系统的领地。

背景:某系统的日志脱敏靠一张人工维护的"敏感字段对照表"(字段名→脱敏规则),三个月里两次漏更新——新字段上线,脱敏表没人改,用户手机号进了日志被安全团队通报。操作:引入自定义 option log_mask(枚举值:明文、部分遮蔽、完全丢弃),定义进公共 annotations 包;全部消息的敏感字段补标注(phone 标部分遮蔽、token 标完全丢弃);日志库改造——序列化消息进日志前用反射遍历字段(下一节的循环),读到 mask 标注就应用对应规则;CI 加一条检查:新增 string 字段若未标注 log_mask,构建警告。结果:脱敏规则与契约同源演进,新字段从上线第一天起行为正确;对照表删除,漏更新类事故结构性消失。解读:这个案例是 option 机制的教科书应用——领域知识(敏感性)刻进契约、消费方(日志库、CI)从描述符读取、单一事实源消灭了人工同步。变式:同一套标注还能喂给第 4 章的文档插件(文档里印敏感标记)与数据团队(数仓入库时按标注决定是否落地原值)——消费方越多,刻写的复利越高。
铭文刻好了,下一节学怎么读整块石碑:运行时反射让代码在不认识具体类型的情况下遍历、读取、修改任意消息。