Protubuf 定义复杂格式的 message

1 摘要

什么是 protobuf? Java Spring boot 如何集成 protobuf? 具体可参考以下博客:

Spring boot 2.5 集成 Google protocol buffer

Protobuf 官方文档:

https://developers.google.com/protocol-buffers

https://developers.google.com/protocol-buffers/docs/overview

本文将介绍如何定义较为复杂格式的 protobuf 文件,包括对象字段,列表字段,枚举类等

2 核心代码

2.1 protobuf 文件
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/protobuf/Student.proto
syntax = "proto3";

option optimize_for = CODE_SIZE;
option java_package = "com.ljq.demo.springboot.mybatisplus.model.entity";
option java_outer_classname = "StudentPb";

// 学生信息
message Student {

  // id
  int64 id = 1;
  // 姓名
  string name = 2;
  // 出生日期(时间戳)
  int32 birth_date = 3;
}

// 班级信息
message ClassInfo {

  // id
  int64 id = 1;
  // 年级
  int32 grade = 2;
  // 班级编号
  int32 number = 3;

}

// 学生选修课
message ElectiveCourse {

  // 学生 id
  int64 stu_id = 1;
  // 课程名称
  repeated string course_names = 2;

}

// 学生班级信息
message StudentClass {

  // id
  int64 id = 1;
  // 学生信息
  Student student = 2;
  // 班级信息
  ClassInfo class_info = 3;

}

// 性别
message StudentSex {
  enum Sex{
    MAN = 0;
    WOMEN = 1;
  }
  Sex sex = 2;
}

// 教师信息
message Teacher {
  // id
  uint64 id = 1;
  // 姓名
  string name = 2;
}

简要说明:

ElectiveCourse 选修课 message 中「课程名称」是属于列表形式的

StudentClass 学生班级信息中包含 StudentClassInfo 对象

StudentSex 学生性别是枚举类的示例,枚举类元素与其他 message 元素定义的区别在于枚举类的元素下标是从 0 开始的。

proto 文件定义好之后需要编译,然后才能生成对应的 Java 类

2.2 测试示例

在生成 Java 类之后进行测试

./demo-mybatis-plus/src/test/java/com/ljq/demo/springboot/mybatisplus/model/entity/StudentPbTest.java
package com.ljq.demo.springboot.mybatisplus.model.entity;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.ljq.demo.springboot.mybatisplus.model.param.student.StudentReceiveParam;
import org.junit.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class StudentPbTest {


    /**
     * 测试-赋值与取值
     */
    @Test
    public void value() {
        // 赋值
        StudentPb.Student student = StudentPb.Student.newBuilder()
                .setId(111L)
                .setName("张三")
                .setBirthDate(1628233076)
                .build();
        System.out.println(student.toString());
        // 测试结果:
//        id: 111
//        name: "\345\274\240\344\270\211"
//        birth_date: 1628233076

        // 测试结论: 字符串的 toString 为编码

        // 取值
        System.out.println("id: " + student.getId());
        System.out.println("name: " + student.getName());
        System.out.println("birth_date: " + student.getBirthDate());
        // 测试结果:
//        id: 111
//        name: 张三
//        birth_date: 1628233076

        // 测试结论: 逐个属性取值不存在编码问题

        // BeanUtil 属性赋值测试
        StudentReceiveParam receiveParam = new StudentReceiveParam();
        BeanUtil.copyProperties(student, receiveParam, CopyOptions.create().ignoreError().ignoreNullValue());
        System.out.println(receiveParam);
        // 测试结果:
//        StudentReceiveParam(id=null, name=null, birth_date=null)

        // 测试结论: Protobuf 生成的 java 类无法使用 beanUtil 进行属性复制
    }

    /**
     * 测试-数据转换
     */
    @Test
    public void convert() throws IOException {
        StudentPb.Student student = StudentPb.Student.newBuilder()
                .setId(111L)
                .setName("张三")
                .setBirthDate(1628233076).build();
        // 转化为字节数组
        byte[] bytes = student.toByteArray();

        // 写入本地
        String localPath = "/Users/ljq/Downloads/student.bin";
        Files.write(Paths.get(localPath), bytes);

        // 从流中读取数据
        byte[] readBytes = Files.readAllBytes(Paths.get(localPath));
        StudentPb.Student readStudent = StudentPb.Student.parseFrom(readBytes);
        // 取值
        System.out.println("id: " + readStudent.getId());
        System.out.println("name: " + readStudent.getName());
        System.out.println("birth_date: " + readStudent.getBirthDate());
    }

    /**
     * 测试-列表字段
     */
    @Test
    public void list() {
        // 赋值
        StudentPb.ElectiveCourse electiveCourse = StudentPb.ElectiveCourse.newBuilder()
                .setStuId(6L)
                .addCourseNames("物理")
                .addCourseNames("化学")
                .addCourseNames("生物")
                .build();
        // 取值
        System.out.println("Student id: " + electiveCourse.getStuId());
        List<String> courseNameList = electiveCourse.getCourseNamesList();
        System.out.println("elective course: ");
        courseNameList.stream().forEach(s -> System.out.println(s));

    }

    /**
     * 测试-字段为对象
     */
    @Test
    public void object() {
        // 赋值
        StudentPb.StudentClass studentClass = StudentPb.StudentClass.newBuilder()
                .setId(7L)
                .setStudent(StudentPb.Student.newBuilder()
                        .setId(123L)
                        .setName("德玛西亚")
                        .setBirthDate(1628233076).build())
                .setClassInfo(StudentPb.ClassInfo.newBuilder()
                        .setId(456L)
                        .setGrade(3)
                        .setNumber(2).build())
                .build();
        // 取值
        System.out.println("id: " + studentClass.getId());
        // 学生信息
        System.out.println("student id: " + studentClass.getStudent().getId());
        System.out.println("student name: " + studentClass.getStudent().getName());
        System.out.println("student birth_date: " + studentClass.getStudent().getBirthDate());
        // 班级信息
        System.out.println("class id: " + studentClass.getClassInfo().getId());
        System.out.println("class grade: " + studentClass.getClassInfo().getGrade());
        System.out.println("class number: " + studentClass.getClassInfo().getNumber());

    }

    /**
     * 测试-枚举类
     */
    @Test
    public void enums() {
        StudentPb.StudentSex studentSex = StudentPb.StudentSex.newBuilder()
                .setSex(StudentPb.StudentSex.Sex.MAN)
                .build();
        // 取值
        System.out.println("student sex: " + studentSex.getSex());
        System.out.println("student sex value: " + studentSex.getSexValue());
        // 测试结果
//        student sex: MAN
//        student sex value: 0

        // 测试结论: 直接获取枚举类,返回的是枚举对象/字符串;获取枚举类的值,返回的是枚举类中对应的属性值
        //  protobuf 在生成枚举类的时候会默认给每一个枚举值赋值为整数,从 0 开始



    }

}

3 推荐参考资料

官方文档 https://developers.google.com/protocol-buffers

官方文档 https://developers.google.com/protocol-buffers/docs/overview

官方文档 Protocol Buffer Basics: Java

4 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值