Protobuf在java中的简单使用实例

TTprotobuf是一种跨语言的数据转换协议,由google开源的,已支持大部份语言。在一般的数据交互过程中都是使用json,xml等来做数据的转换,这其中涉及复杂的解析与序列化反序列化问题,如果在大量数据并发请求时,也会导致性能问题。protobuf采用Varint编码技术,在某种程度上减少数字的字节数,关于Varint的实现原理可以参考:

https://segmentfault.com/a/1190000020500985?utm_source=tag-newest

由于protobuf是跨语言的,所以用不同的语言序列化对象后,生成一段字节码,之后可以其他任何语言反序列化并自用,大大方便了跨语言的通讯,同时也提高了效率。

本文主要是Protobuf针对java在后台开发的应用,用一个简单的实例工程来实践。

 在开始项目之前,先安装protobuf插件,方便后面编写proto文件

  • 新建一个maven工程,在项目main下新建目录,命名为proto
  • 并导入以下pom依赖(本项目使用protobuf版本为3.5.1):
<dependencies>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java-util</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.grpc/grpc-all -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-all</artifactId>
        <version>1.11.0</version>
    </dependency>
</dependencies>
<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.5.0.Final</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.5.0</version>
            <configuration>
                <protocArtifact>
                    com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}
                </protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>
                    io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}
                </pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
  • 需要先自行配置好maven
  • 新建一个包:com.study.myprotobuf.dto:
  • 新建两个proto文件:student.proto,course.proto

  •  编写proto文件之前,可以先大概百度了解一下proto的语法,实际也跟java或c的类似,有一个头部定义包,文件名,引入其他proto文件等。在这里说明一下,proto文件实际不是业务端要用到的,而是一个模板文件,google开源支持的语言都可以使用这个proto文件。
  • 以下是两个proto文件的内容,课程模板与学生模板文件:
// 指定模板类的包路径
option java_package = "com.study.myprotobuf.dto";
// 指定模板类的名称,名称必须是有实际业务意义的
option java_outer_classname = "CourseProto";

// 定义课程
message course{
  // 课程名称
  required string name = 1;
  // 课程分数
  required int32 score = 2;
}
// 指定模板类的包路径
option java_package = "com.study.myprotobuf.dto";
// 指定模板类的名称,名称必须是有实际业务意义的
option java_outer_classname = "StudentProto";
// 引入课程模板
import "course.proto";

// 定义学生模板
message student{
  // 学生名称
  required string name = 1;
  // 学生年龄
  required int32 age = 2;
  // 学生所修课程,可能是多门,是一组数组
  repeated course course = 3;
}
  • 如果maven配置成功,可以打开maven的Plugins下的protobuf节点中的compile,双击进行编译:

  • 编译成功后,生成target目录,找到对应的proto类,如下所示: 
  • 以上生成java代码类方式也可以通过命令的方式:

    从github下载protoc工具:

    https://github.com/protocolbuffers/protobuf/releases/tag/v3.5.1

    添加环境变量:

     打开cmd命令窗口,输入protoc --version,查看版本号:

    在本地磁盘新建存放proto文件和生成java类的文件目录:

    D:/protofile(proto文件)

    D:/protojava(生成的java类)

    在cmd命令窗口输入以下命令:

    protoc -I=D:/protojava --java_out=D:/protojava D:/protofile/teacher.proto --proto_path=D:/protofile

    执行成功后可以D:/protofile目录中生成protobuf的java类文件。

  •  将这两个java类复制到之前新建的dto包中,此时,可以看到google生成的对象proto的源代码,里面实际包含了模板类的创建,序列化,反序列化和json转换等方法
  • 新建一个test包,新增一个类TestMyProtobuf进行测试,完成的代码如下:
package com.study.myprotobuf.test;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.study.myprotobuf.dto.CourseProto;
import com.study.myprotobuf.dto.StudentProto;

import java.util.Arrays;

public class TestMyProtobuf {
    public static void main(String[] args) {
        // 生成课程1对象
        CourseProto.course.Builder courseBuilder1 = CourseProto.course.newBuilder();
        courseBuilder1.setName("Java");
        courseBuilder1.setScore(99);
        CourseProto.course course1 = courseBuilder1.build();

        // 生成课程2对象
        CourseProto.course.Builder courseBuilder2 = CourseProto.course.newBuilder();
        courseBuilder2.setName("Python");
        courseBuilder2.setScore(98);
        CourseProto.course course2 = courseBuilder2.build();

        // 生成学生对象
        StudentProto.student.Builder studentBuilder = StudentProto.student.newBuilder();
        studentBuilder.setName("Lucy");
        studentBuilder.setAge(23);
        studentBuilder.addCourse(0,course1);
        studentBuilder.addCourse(1,course2);
        StudentProto.student student = studentBuilder.build();

        // proto对象
        System.out.println("The student object is: \n" + student);
        // 序列化
        byte[] studentByte = student.toByteArray();
        System.out.println("The student after encode is:\n" + Arrays.toString(studentByte));
        try {
            // 反序列化
            StudentProto.student newStudent = StudentProto.student.parseFrom(studentByte);
            System.out.println("The student after decode is:\n" + newStudent);
            // 转换json
            System.out.println("The student json format is:\n" + JsonFormat.printer().print(student));
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:


The student object is: 
name: "Lucy"
age: 23
course {
  name: "Java"
  score: 99
}
course {
  name: "Python"
  score: 98
}

The student after encode is:
[10, 4, 76, 117, 99, 121, 16, 23, 26, 8, 10, 4, 74, 97, 118, 97, 16, 99, 26, 10, 10, 6, 80, 121, 116, 104, 111, 110, 16, 98]
The student after decode is:
name: "Lucy"
age: 23
course {
  name: "Java"
  score: 99
}
course {
  name: "Python"
  score: 98
}

The student json format is:
{
  "name": "Lucy",
  "age": 23,
  "course": [{
    "name": "Java",
    "score": 99
  }, {
    "name": "Python",
    "score": 98
  }]
}

Process finished with exit code 0

至此,proto基本的使用就已经结束了,实际也是将对象通过模板的方式去定义,以便于通过不同的语言去调用,并且从反序列化的过程中获取。

以上为个在实践过程中的一些操作,如有错误之处,请指出,欢迎讨论,共同进步!谢谢!

以下是一个基于Protobuf动态解析在Java的示例程序: 1. 下载protobuf-java.jar和protobuf-java-util.jar包并添加到classpath。 2. 定义一个proto文件,例如: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; repeated string hobbies = 3; } ``` 3. 使用protoc编译proto文件,生成Java类: ``` protoc --java_out=. person.proto ``` 4. 在Java动态解析protobuf消息: ``` import com.google.protobuf.DynamicMessage; import com.google.protobuf.Descriptors; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.ByteString; public class DynamicProtoExample { public static void main(String[] args) throws Exception { Descriptors.Descriptor descriptor = Person.getDescriptor(); // Person是生成的Java类 DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); builder.setField(descriptor.findFieldByName("name"), "Tom"); builder.setField(descriptor.findFieldByName("age"), 18); builder.addRepeatedField(descriptor.findFieldByName("hobbies"), "reading"); builder.addRepeatedField(descriptor.findFieldByName("hobbies"), "swimming"); DynamicMessage message = builder.build(); ByteString data = message.toByteString(); DynamicMessage parsedMessage = DynamicMessage.parseFrom(descriptor, data); String name = parsedMessage.getField(descriptor.findFieldByName("name")).toString(); int age = (int) parsedMessage.getField(descriptor.findFieldByName("age")); List<String> hobbiesList = parsedMessage.getRepeatedField(descriptor.findFieldByName("hobbies")); System.out.println("Name: " + name + ", Age: " + age + ", Hobbies: " + hobbiesList); } } ``` 这个程序动态创建了一个Person消息,将它转化为字节数组,然后再将其解析回Person消息,并从获取字段值。 这是一个简单的示例,但它演示了如何使用Protobuf动态解析在Java创建和解析消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值