的使用介绍ProtocolBuffer是Google出品的一种对象序列化方法。它体积小,传输速度快,深受大家的喜爱。Protobuf是一个独立于平台和语言的协议。通过protobuf的定义文件,可以方便的转换成多种语言的实现,非常方便。今天给大家介绍一下protobuf的基本使用以及结合java的具体案例。为什么要使用protobuf?我们知道数据在网络中是以二进制形式传输的。一般我们用字节来表示。一个字节是8位。如果要在网络上传输对象,一般需要对对象进行序列化。序列化的目的是将对象转换成字节数组,在网络上传输。接收方接收到字节数组后,将字节数组反序列化,最后转成java中的对象。那么java对象的序列化可能有几种方式:使用JDK自带的对象序列化,但是JDK自带的序列化存在一些问题,而且这种序列化方式只适用于java程序之间的传输,如果是一个非java程序,比如PHP或者GO,那么序列化是不通用的。您还可以自定义序列化协议。这种方式比较灵活,但是不够通用,实现起来比较复杂,可能会出现意想不到的问题。将数据转换为XML或JSON以进行传输。XML和JSON的优点是都有可以区分对象的起始符号,通过判断这些符号的位置就可以读出完整的对象。但是XML和JSON的缺点都是转换后的数据比较大。它还在反序列化期间消耗更多资源。所以我们需要一种新的序列化方式,它就是protobuf,它是一种灵活、高效、自动化的解决方案。通过编写一个.proto数据结构定义文件,然后调用protobuf编译器,生成相应的类,实现了对protobuf数据的高效二进制格式的自动编码和解析。生成的类为定义文件中的数据字段提供getter和setter方法,并提供读写的处理细节。重要的是,protobuf是向前兼容的,这意味着旧的二进制文件也可以使用最新的协议读取。定义.proto文件。.proto文件中的定义是您将序列化的消息对象。让我们来看一个基本的student.proto文件,它定义了学生对象最基本的属性。先看一个比较简单的.proto文件:syntax="proto3";packagecom.flydean;optionjava_multiple_files=true;optionjava_package="com.flydean.tutorial.protos";optionjava_outer_classname="StudentListProtos";messageStudent{optional字符串名称=1;可选的int32id=2;可选字符串email=3;枚举PhoneType{MOBILE=0;家=1;}messagePhoneNumber{可选字符串number=1;可选PhoneType类型=2;}repeatedPhoneNumberphones=4;}messageStudentList{repeatedStudentstudent=1;}第一行定义了protobuf中使用的语法协议,默认是proto2,因为最新的协议是proto3,所以这里以proto3为例。然后我们定义我们所在的包,指的是编译时生成文件的包。这是一个命名空间。虽然后面定义了java_package,但是为了和非java语言的协议冲突,还是需要定义package的。然后是java程序专用的三个选项。java_multiple_files、java_package和java_outer_classname。其中,java_multiple_files是指编译后的java文件个数。如果为真,那么一个java对象就是一个类。如果为false,则定义的java对象将包含在同一个文件中。java_package指定生成的类应该使用的Java包名称。如果未明确指定,将使用先前定义的包的值。java_outer_classname选项定义将表示此文件的包装器类的类名。如果java_outer_classname没有赋值,它将通过将文件名转换为大驼峰大小写来生成。例如,“student.proto”默认使用“Student”作为包装类名。下一部分是消息的定义。对于简单类型,可以使用bool、int32、float、double和string来定义字段的类型。在上面的示例中,我们还使用了复杂的复合属性和嵌套类型。还定义了一个枚举类。上面我们为每个属性值分配了一个ID,这个ID是二进制编码中使用的唯一“标签”。因为在protobuf中标记数字1-15比标记16以上的数字占用更少的字节空间,作为优化,这些标记1-15通常用于常用或重复的元素,而标记16和更高的标记用于不常用的可选元素。再看字段修饰符,有optional、repeated和required三种修饰符。optional表示该字段是可选的,可以设置也可以不设置。如果未设置,将使用默认值。对于简单类型,我们可以自定义默认值。如果没有,系统将使用默认值。对于系统默认,数字为0,字符串为空字符串,布尔值为false。repeated表示这个字段可以重复,这个重复其实就是一个数组结构。required表示该字段是必需的。如果该字段没有值,则该字段将被视为未初始化。尝试构造未初始化的消息将抛出RuntimeException,解析未初始化的消息将抛出IOException。请注意,Proto3不支持必填字段。编译协议文件定义好proto文件后,就可以使用protoc命令编译了。protoc是protobuf提供的编译器。一般情况下可以直接从github的release库下载。如果不想直接下载,或者官方库中没有你需要的版本,可以使用源码直接编译。protoc使用的命令如下:protoc--experimental_allow_proto3_optional-I=$SRC_DIR--java_out=$DST_DIR$SRC_DIR/student.proto如果编译proto3,需要加上--experimental_allow_proto3_optional选项。让我们运行上面的代码。你会发现com.flydean.tutorial.protos包中生成了5个文件。它们是:Student.javaStudentList.javaStudentListOrBuilder.javaStudentListProtos.javaStudentOrBuilder.java其中,StudentListOrBuilder和StudentOrBuilder是两个接口,Student和StudentList是这两个类的实现。生成文件详解在proto文件中,我们主要定义了两个类Student和StudentList,其中定义了一个内部类Builder。以Student为例,看这两个类的定义:publicfinalclassStudentextendscom.google.protobuf.GeneratedMessageV3implementsStudentOrBuilderpublicstaticfinalclassBuilderextendscom.google.protobuf.GeneratedMessageV3.Builder
