Protocol Buffers(简称 protobuf)是 Google 开发的
高效二进制序列化工具
,用于结构化数据的存储和传输。核心特性
特性 | 说明 |
---|---|
跨语言支持 | 支持 Java、C++、Python、Go 等主流语言。 |
高效编解码 | 二进制格式,比 JSON/XML 更小、更快。 |
强类型约束 | 通过 .proto 文件定义数据结构,避免运行时错误。 |
向后兼容 | 支持字段扩展(新增字段不影响旧代码)。 |
使用指南
开发环境
- windows
- jetbrains idea
- maven_3.9.7
- jdk_8
- protobuf_31.1
安装编译器(protoc)
下载地址
: protobuf_v31.1
配置
- 将下载的压缩包解压出来,新增环境变量protobuf=文件夹路径
- 编辑环境变量Path,新增%protobuf%\bin
验证
# 打开cmd命令行,执行以下命令 protoc --version # 输出结果如下,表示安装成功 libprotoc 31.1
定义数据结构
- 新建person.proto文件
// person.proto syntax = "proto3"; message Person { string name = 1; int32 id = 2; repeated string emails = 3; }
编译
手动执行命令
# 打开cmd命令行,切换到person.proto文件目录下,执行以下命令 protoc --java_out=./ ./person.proto # 当前目录下会生成PersonOuterClass.java文件 # 将PersonOuterClass.java复制到对应项目目录就可以使用了 # PersonOuterClass.java默认没有package路径,需要手动加一下
maven插件编译
- 在pom.xml中加入以下代码,执行maven的clean compile
<build> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> <configuration> <!-- 从 Path 中查找 --> <protocExecutable>protoc</protocExecutable> <!-- 或直接指定路径 --> <!--<protocExecutable>xxx\bin\protoc.exe</protocExecutable>--> </configuration> </plugin> </plugins> </build>
- 插件默认读取文件夹
/src/main/proto
下的proto文件去编译 - 在文件夹
target/generated-sources/protobuf/java
下会生成对应pojo的protobuf操作类
注意:
- 若执行protoc命令之后idea报如下错误:
Module 'my-test' production: java.lang.ClassCastException: class org.jetbrains.jps.builders.java.dependencyView.TypeRepr$PrimitiveType cannot be cast to class org.jetbrains.jps.builders.java.dependencyView.TypeRepr$ClassType (org.jetbrains.jps.builders.java.dependencyView.TypeRepr$PrimitiveType and org.jetbrains.jps.builders.java.dependencyView.TypeRepr$ClassType are in unnamed module of loader java.net.URLClassLoader @2f2c9b19)
。则清除一下idea的缓存,然后执行rebuild- 若idea的terminal或者maven执行compile的时候报错未找到protoc命令,则可以在terminal中执行以下命令排查idea是否读取到最新的Path。若打印的Path值没有protoc的路径,则重启一下电脑。
# terminal中使用的是powershell的话执行 echo $env:path # terminal中使用的是cmd的话执行 echo %path%
编译结果示例
// Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: person.proto // Protobuf Java Version: 4.31.1 @com.google.protobuf.Generated public final class PersonOuterClass { private PersonOuterClass() {} static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 31, /* patch= */ 1, /* suffix= */ "", PersonOuterClass.class.getName()); } public static void registerAllExtensions( com.google.protobuf.ExtensionRegistryLite registry) { } public static void registerAllExtensions( com.google.protobuf.ExtensionRegistry registry) { registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } public interface PersonOrBuilder extends // @@protoc_insertion_point(interface_extends:Person) com.google.protobuf.MessageOrBuilder { /** * <code>string name = 1;</code> * @return The name. */ java.lang.String getName(); /** * <code>string name = 1;</code> * @return The bytes for name. */ com.google.protobuf.ByteString getNameBytes(); /** * <code>int32 id = 2;</code> * @return The id. */ int getId(); /** * <code>repeated string emails = 3;</code> * @return A list containing the emails. */ java.util.List<java.lang.String> getEmailsList(); /** * <code>repeated string emails = 3;</code> * @return The count of emails. */ int getEmailsCount(); /** * <code>repeated string emails = 3;</code> * @param index The index of the element to return. * @return The emails at the given index. */ java.lang.String getEmails(int index); /** * <code>repeated string emails = 3;</code> * @param index The index of the value to return. * @return The bytes of the emails at the given index. */ com.google.protobuf.ByteString getEmailsBytes(int index); } /** * Protobuf type {@code Person} */ public static final class Person extends com.google.protobuf.GeneratedMessage implements // @@protoc_insertion_point(message_implements:Person) PersonOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, /* minor= */ 31, /* patch= */ 1, /* suffix= */ "", Person.class.getName()); } // Use Person.newBuilder() to construct. private Person(com.google.protobuf.GeneratedMessage.Builder<?> builder) { super(builder); } private Person() { name_ = ""; emails_ = com.google.protobuf.LazyStringArrayList.emptyList(); } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return PersonOuterClass.internal_static_Person_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { return PersonOuterClass.internal_static_Person_fieldAccessorTable .ensureFieldAccessorsInitialized( PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class); } public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; /** * <code>string name = 1;</code> * @return The name. */ @java.lang.Override public java.lang.String getName() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); name_ = s; return s; } } /** * <code>string name = 1;</code> * @return The bytes for name. */ @java.lang.Override public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int ID_FIELD_NUMBER = 2; private int id_ = 0; /** * <code>int32 id = 2;</code> * @return The id. */ @java.lang.Override public int getId() { return id_; } public static final int EMAILS_FIELD_NUMBER = 3; @SuppressWarnings("serial") private com.google.protobuf.LazyStringArrayList emails_ = com.google.protobuf.LazyStringArrayList.emptyList(); /** * <code>repeated string emails = 3;</code> * @return A list containing the emails. */ public com.google.protobuf.ProtocolStringList getEmailsList() { return emails_; } /** * <code>repeated string emails = 3;</code> * @return The count of emails. */ public int getEmailsCount() { return emails_.size(); } /** * <code>repeated string emails = 3;</code> * @param index The index of the element to return. * @return The emails at the given index. */ public java.lang.String getEmails(int index) { return emails_.get(index); } /** * <code>repeated string emails = 3;</code> * @param index The index of the value to return. * @return The bytes of the emails at the given index. */ public com.google.protobuf.ByteString getEmailsBytes(int index) { return emails_.getByteString(index); } private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized == 1) return true; if (isInitialized == 0) return false; memoizedIsInitialized = 1; return true; } @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } if (id_ != 0) { output.writeInt32(2, id_); } for (int i = 0; i < emails_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 3, emails_.getRaw(i)); } getUnknownFields().writeTo(output); } @java.lang.Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } if (id_ != 0) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(2, id_); } { int dataSize = 0; for (int i = 0; i < emails_.size(); i++) { dataSize += computeStringSizeNoTag(emails_.getRaw(i)); } size += dataSize; size += 1 * getEmailsList().size(); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; } @java.lang.Override public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } if (!(obj instanceof PersonOuterClass.Person)) { return super.equals(obj); } PersonOuterClass.Person other = (PersonOuterClass.Person) obj; if (!getName() .equals(other.getName())) return false; if (getId() != other.getId()) return false; if (!getEmailsList() .equals(other.getEmailsList())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @java.lang.Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (37 * hash) + ID_FIELD_NUMBER; hash = (53 * hash) + getId(); if (getEmailsCount() > 0) { hash = (37 * hash) + EMAILS_FIELD_NUMBER; hash = (53 * hash) + getEmailsList().hashCode(); } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; } public static PersonOuterClass.Person parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static PersonOuterClass.Person parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static PersonOuterClass.Person parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static PersonOuterClass.Person parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static PersonOuterClass.Person parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static PersonOuterClass.Person parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static PersonOuterClass.Person parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } public static PersonOuterClass.Person parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input, extensionRegistry); } public static PersonOuterClass.Person parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } public static PersonOuterClass.Person parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } public static PersonOuterClass.Person parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } public static PersonOuterClass.Person parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input, extensionRegistry); } @java.lang.Override public Builder newBuilderForType() { return newBuilder(); } public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } public static Builder newBuilder(PersonOuterClass.Person prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override public Builder toBuilder() { return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); } @java.lang.Override protected Builder newBuilderForType( com.google.protobuf.GeneratedMessage.BuilderParent parent) { Builder builder = new Builder(parent); return builder; } /** * Protobuf type {@code Person} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder<Builder> implements // @@protoc_insertion_point(builder_implements:Person) PersonOuterClass.PersonOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return PersonOuterClass.internal_static_Person_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { return PersonOuterClass.internal_static_Person_fieldAccessorTable .ensureFieldAccessorsInitialized( PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class); } // Construct using PersonOuterClass.Person.newBuilder() private Builder() { } private Builder( com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); } @java.lang.Override public Builder clear() { super.clear(); bitField0_ = 0; name_ = ""; id_ = 0; emails_ = com.google.protobuf.LazyStringArrayList.emptyList(); return this; } @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return PersonOuterClass.internal_static_Person_descriptor; } @java.lang.Override public PersonOuterClass.Person getDefaultInstanceForType() { return PersonOuterClass.Person.getDefaultInstance(); } @java.lang.Override public PersonOuterClass.Person build() { PersonOuterClass.Person result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @java.lang.Override public PersonOuterClass.Person buildPartial() { PersonOuterClass.Person result = new PersonOuterClass.Person(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } private void buildPartial0(PersonOuterClass.Person result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.name_ = name_; } if (((from_bitField0_ & 0x00000002) != 0)) { result.id_ = id_; } if (((from_bitField0_ & 0x00000004) != 0)) { emails_.makeImmutable(); result.emails_ = emails_; } } @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof PersonOuterClass.Person) { return mergeFrom((PersonOuterClass.Person)other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(PersonOuterClass.Person other) { if (other == PersonOuterClass.Person.getDefaultInstance()) return this; if (!other.getName().isEmpty()) { name_ = other.name_; bitField0_ |= 0x00000001; onChanged(); } if (other.getId() != 0) { setId(other.getId()); } if (!other.emails_.isEmpty()) { if (emails_.isEmpty()) { emails_ = other.emails_; bitField0_ |= 0x00000004; } else { ensureEmailsIsMutable(); emails_.addAll(other.emails_); } onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; } @java.lang.Override public final boolean isInitialized() { return true; } @java.lang.Override public Builder mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { if (extensionRegistry == null) { throw new java.lang.NullPointerException(); } try { boolean done = false; while (!done) { int tag = input.readTag(); switch (tag) { case 0: done = true; break; case 10: { name_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 case 16: { id_ = input.readInt32(); bitField0_ |= 0x00000002; break; } // case 16 case 26: { java.lang.String s = input.readStringRequireUtf8(); ensureEmailsIsMutable(); emails_.add(s); break; } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag } break; } // default: } // switch (tag) } // while (!done) } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.unwrapIOException(); } finally { onChanged(); } // finally return this; } private int bitField0_; private java.lang.Object name_ = ""; /** * <code>string name = 1;</code> * @return The name. */ public java.lang.String getName() { java.lang.Object ref = name_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); name_ = s; return s; } else { return (java.lang.String) ref; } } /** * <code>string name = 1;</code> * @return The bytes for name. */ public com.google.protobuf.ByteString getNameBytes() { java.lang.Object ref = name_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * <code>string name = 1;</code> * @param value The name to set. * @return This builder for chaining. */ public Builder setName( java.lang.String value) { if (value == null) { throw new NullPointerException(); } name_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** * <code>string name = 1;</code> * @return This builder for chaining. */ public Builder clearName() { name_ = getDefaultInstance().getName(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** * <code>string name = 1;</code> * @param value The bytes for name to set. * @return This builder for chaining. */ public Builder setNameBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); name_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } private int id_ ; /** * <code>int32 id = 2;</code> * @return The id. */ @java.lang.Override public int getId() { return id_; } /** * <code>int32 id = 2;</code> * @param value The id to set. * @return This builder for chaining. */ public Builder setId(int value) { id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** * <code>int32 id = 2;</code> * @return This builder for chaining. */ public Builder clearId() { bitField0_ = (bitField0_ & ~0x00000002); id_ = 0; onChanged(); return this; } private com.google.protobuf.LazyStringArrayList emails_ = com.google.protobuf.LazyStringArrayList.emptyList(); private void ensureEmailsIsMutable() { if (!emails_.isModifiable()) { emails_ = new com.google.protobuf.LazyStringArrayList(emails_); } bitField0_ |= 0x00000004; } /** * <code>repeated string emails = 3;</code> * @return A list containing the emails. */ public com.google.protobuf.ProtocolStringList getEmailsList() { emails_.makeImmutable(); return emails_; } /** * <code>repeated string emails = 3;</code> * @return The count of emails. */ public int getEmailsCount() { return emails_.size(); } /** * <code>repeated string emails = 3;</code> * @param index The index of the element to return. * @return The emails at the given index. */ public java.lang.String getEmails(int index) { return emails_.get(index); } /** * <code>repeated string emails = 3;</code> * @param index The index of the value to return. * @return The bytes of the emails at the given index. */ public com.google.protobuf.ByteString getEmailsBytes(int index) { return emails_.getByteString(index); } /** * <code>repeated string emails = 3;</code> * @param index The index to set the value at. * @param value The emails to set. * @return This builder for chaining. */ public Builder setEmails( int index, java.lang.String value) { if (value == null) { throw new NullPointerException(); } ensureEmailsIsMutable(); emails_.set(index, value); bitField0_ |= 0x00000004; onChanged(); return this; } /** * <code>repeated string emails = 3;</code> * @param value The emails to add. * @return This builder for chaining. */ public Builder addEmails( java.lang.String value) { if (value == null) { throw new NullPointerException(); } ensureEmailsIsMutable(); emails_.add(value); bitField0_ |= 0x00000004; onChanged(); return this; } /** * <code>repeated string emails = 3;</code> * @param values The emails to add. * @return This builder for chaining. */ public Builder addAllEmails( java.lang.Iterable<java.lang.String> values) { ensureEmailsIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( values, emails_); bitField0_ |= 0x00000004; onChanged(); return this; } /** * <code>repeated string emails = 3;</code> * @return This builder for chaining. */ public Builder clearEmails() { emails_ = com.google.protobuf.LazyStringArrayList.emptyList(); bitField0_ = (bitField0_ & ~0x00000004);; onChanged(); return this; } /** * <code>repeated string emails = 3;</code> * @param value The bytes of the emails to add. * @return This builder for chaining. */ public Builder addEmailsBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); ensureEmailsIsMutable(); emails_.add(value); bitField0_ |= 0x00000004; onChanged(); return this; } // @@protoc_insertion_point(builder_scope:Person) } // @@protoc_insertion_point(class_scope:Person) private static final PersonOuterClass.Person DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new PersonOuterClass.Person(); } public static PersonOuterClass.Person getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser<Person> PARSER = new com.google.protobuf.AbstractParser<Person>() { @java.lang.Override public Person parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { Builder builder = newBuilder(); try { builder.mergeFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(builder.buildPartial()); } catch (com.google.protobuf.UninitializedMessageException e) { throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); } catch (java.io.IOException e) { throw new com.google.protobuf.InvalidProtocolBufferException(e) .setUnfinishedMessage(builder.buildPartial()); } return builder.buildPartial(); } }; public static com.google.protobuf.Parser<Person> parser() { return PARSER; } @java.lang.Override public com.google.protobuf.Parser<Person> getParserForType() { return PARSER; } @java.lang.Override public PersonOuterClass.Person getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } private static final com.google.protobuf.Descriptors.Descriptor internal_static_Person_descriptor; private static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_Person_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } private static com.google.protobuf.Descriptors.FileDescriptor descriptor; static { java.lang.String[] descriptorData = { "\n\014person.proto\"2\n\006Person\022\014\n\004name\030\001 \001(\t\022\n" + "\n\002id\030\002 \001(\005\022\016\n\006emails\030\003 \003(\tb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] { }); internal_static_Person_descriptor = getDescriptor().getMessageTypes().get(0); internal_static_Person_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_Person_descriptor, new java.lang.String[] { "Name", "Id", "Emails", }); descriptor.resolveAllFeaturesImmutable(); } // @@protoc_insertion_point(outer_class_scope) }
执行
从上面编译出来的文件中可以找到这行注释// Protobuf Java Version: 4.31.1
,表示protobuf对应java依赖的版本。
- 在pom.xml中引入依赖
<!-- Protobuf Java --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>4.31.1</version> </dependency>
- java测试代码
public static void main(String[] args) throws InvalidProtocolBufferException { // 构造对象 PersonOuterClass.Person person = PersonOuterClass.Person.newBuilder() .setName("Alice") .setId(123) .addEmails("alice@example.com") .build(); // 序列化为字节数组 byte[] bytes = person.toByteArray(); // 反序列化 PersonOuterClass.Person parsedPerson = PersonOuterClass.Person.parseFrom(bytes); System.out.println(parsedPerson.getName()); System.out.println(parsedPerson.getId()); System.out.println(parsedPerson.getEmails(0)); }
- 执行结果
Alice 123 alice@example.com
性能测试
protobuf和json的性能对比
此处对比protobuf、hutool的json工具类、jackson的性能
- 引入依赖
<!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.8.2</version> </dependency> <!-- jmh性能测试 --> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.37</version> </dependency> <!-- 编译时处理JMH的注解,若不加,编译出来的benchmark注解未被解析,运行时会报错 --> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.37</version> <scope>provided</scope> </dependency> <!-- jackson 依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.15.2</version> </dependency> <!-- hutool 依赖 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.27</version> </dependency>
- 测试的pojo类
public class PersonJson { private String name; private int id; private String[] emails; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String[] getEmails() { return emails; } public void setEmails(String[] emails) { this.emails = emails; } }
- 测试代码
package protobuf; import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.openjdk.jmh.annotations.*; import java.io.IOException; @Warmup(iterations = 2, time = 1) @Measurement(iterations = 2, time = 1) @Fork(2) @State(Scope.Thread) public class ProtobufBenchmark { private PersonOuterClass.Person person; private byte[] serializedData; private String jsonStr; private PersonJson personJson; private ObjectMapper objectMapper = new ObjectMapper(); @Setup public void setup() { person = PersonOuterClass.Person.newBuilder() .setName("Charlie") .setId(789) .addEmails("charlie@example.com") .build(); serializedData = person.toByteArray(); personJson = new PersonJson(); personJson.setName("Charlie"); personJson.setId(789); personJson.setEmails(new String[]{"charlie@example.com"}); jsonStr = JSONUtil.toJsonStr(personJson); } /** * 测试protobuf序列化 * * @return */ @Benchmark public byte[] testSerialize() { return person.toByteArray(); } /** * 测试protobuf反序列化 * * @return * @throws Exception */ @Benchmark public PersonOuterClass.Person testDeserialize() throws Exception { return PersonOuterClass.Person.parseFrom(serializedData); } /** * 测试hutool-json序列化 * * @return */ @Benchmark public String testJsonSerialize() { return JSONUtil.toJsonStr(personJson); } /** * 测试hutool-json反序列化 * * @return */ @Benchmark public PersonJson testJsonDeserialize() { return JSONUtil.toBean(jsonStr, PersonJson.class); } /** * 测试Jackson序列化 * * @return * @throws JsonProcessingException */ @Benchmark public String testJacksonSerialize() throws JsonProcessingException { return objectMapper.writeValueAsString(personJson); } /** * 测试Jackson反序列化 * * @return * @throws Exception */ @Benchmark public PersonJson testJacksonDeserialize() throws Exception { return objectMapper.readValue(jsonStr, PersonJson.class); } }
运行结果
Benchmark Mode Cnt Score Error Units ProtobufBenchmark.testSerialize thrpt 4 23652174.844 ± 1801935.493 ops/s ProtobufBenchmark.testDeserialize thrpt 4 7830878.513 ± 2204277.060 ops/s ProtobufBenchmark.testJacksonSerialize thrpt 4 5737731.097 ± 975288.754 ops/s ProtobufBenchmark.testJacksonDeserialize thrpt 4 2806124.511 ± 396224.766 ops/s ProtobufBenchmark.testJsonSerialize thrpt 4 256885.571 ± 42109.112 ops/s ProtobufBenchmark.testJsonDeserialize thrpt 4 289602.156 ± 78181.194 ops/s
- protobuf序列化性能是jackson的4倍,反序列化性能也将近3倍
- hutool的json性能就比较差了,所以实际项目中若要使用json,推荐使用jackson
小结
本文介绍了protobuf从安装到使用的全过程,并提供了相应的代码示例。读者可以通过直接运行代码示例直观的学习到protobuf如何使用。最后测试和对比了protobuf、json序列化和反序列化的性能。
这一切,似未曾拥有