你有一份「秘密文件」(`byte[]` 的二进制数据),想把它变成一本「故事书」(你的自定义Java对象DailySortPrice)。
可是这个二进制数据是用「特别的语言」写的,别人不一定看得懂。
Protobuf 就像是一本「词典」,按照 `.proto` 文件的定义将二进制数据翻译成一种「标准语言」(Protobuf对象)。
等你拿到这份标准语言描述的东西后,再按照你的业务逻辑和习惯,把它转换成你的「故事书」(DailySortPrice 对象)格式。
## run
1. protobuf
1. protobuf 中新增标签号 `repeated string propertyValueTags=119;`
2. `protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/PriceEntryProtoBuf.proto`
3. `PriceEntryProtoBufUtils.java`
这是一条使用 `protoc`(Protocol Buffers 编译器)编译 `.proto` 文件的命令。下面对命令各部分进行解释:
```Java
protoc -I={src_dir}/com/qunar/hotel/protobuf/definition \
--java_out={src_dir} \
{src_dir}/com/qunar/hotel/protobuf/definition/PriceEntryProtoBuf.proto
```
1. **protoc**:
`protoc` 是 Google Protocol Buffers 的编译器程序,用于将 `.proto` 文件生成对应语言的代码文件(如 Java、C++、Python 等)。
2. **-I={src_dir}/com/qunar/hotel/protobuf/definition**:
`-I` 是 `--proto_path` 的缩写形式,代表的是 "include path" 或 "import path"。在 `protoc` 命令中,通过 `-I` 或 `--proto_path` 指定一个目录作为 `.proto` 文件的搜索路径。当你的 `.proto` 文件中使用 `import` 语句导入其他 `.proto` 文件时,编译器会从指定的 `-I` 目录开始查找相应的文件。
在本例中,`-I` 指定了 `src_dir/com/qunar/hotel/protobuf/definition` 为包含 `.proto` 文件的目录,这样 `protoc` 就能在这个路径下找到要编译的 `.proto` 文件。
3. **--java_out={src_dir}**:
`--java_out` 参数指定生成的 Java 源代码文件要输出到哪个目录。
在此处,`{src_dir}` 为你的源代码根目录,编译器会在该目录下自动创建对应的 Java 包目录结构,并将生成的 `.java` 文件写入其中。
4. **{src_dir}/com/qunar/hotel/protobuf/definition/PriceEntryProtoBuf.proto**:
这是要编译的 `.proto` 文件的路径。
该 `.proto` 文件中定义了数据结构(如消息类型、枚举)等,当执行此命令后,`protoc` 会依据其定义生成相应的 Java 类文件。
## 为什么不直接从 byte 到业务对象
[适配器模式](适配器模式.md)的思想
- **Input(byte[])**:原始数据(难以直接理解)
- **Protobuf对象**:通过 Protobuf 规范解析出的中间模型(统一标准格式)
- **业务对象**:适配和转换后的最终对象,适合直接在业务逻辑中使用
- **Protobuf对象是中间翻译层**:
Protobuf 编译器根据 `.proto` 文件生成的代码里有 `parseFrom()` 等方法,这些方法可以直接把 byte[]按照 `.proto` 里定义的格式解析成一个清晰的、可访问的对象(`DailySortPriceProtoBuf.DailySortPrice`)。
如果你跳过这一步,`DailySortPrice` 对象就只能看到一串没头没尾的 byte[],不知道应该怎样拆分和解析每个字段。
- **自定义对象可能与协议数据结构不同**:
有时候,你的业务对象 `DailySortPrice` 的属性名、结构、类型可能和 Protobuf 定义的结构有点差异。通过先获得一个 Protobuf 对象,你就可以按照自己的需求有序地「拷贝」和「转换」数据,灵活处理数据类型、默认值、数据清洗等逻辑。这样业务对象不一定要和 Protobuf 定义一模一样的字段和类型。
