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
验证
shell
# 打开cmd命令行,执行以下命令
protoc --version
# 输出结果如下,表示安装成功
libprotoc 31.1
定义数据结构
- 新建person.proto文件
protobuf
// person.proto
syntax = "proto3";
message Person {
string name = 1;
int32 id = 2;
repeated string emails = 3;
}
编译
手动执行命令
shell
# 打开cmd命令行,切换到person.proto文件目录下,执行以下命令
protoc --java_out=./ ./person.proto
# 当前目录下会生成PersonOuterClass.java文件
# 将PersonOuterClass.java复制到对应项目目录就可以使用了
# PersonOuterClass.java默认没有package路径,需要手动加一下
maven插件编译
- 在pom.xml中加入以下代码,执行maven的clean compile
xml
<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的路径,则重启一下电脑。
shell
# terminal中使用的是powershell的话执行
echo $env:path
# terminal中使用的是cmd的话执行
echo %path%
编译结果示例
java
// 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中引入依赖
xml
<!-- Protobuf Java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.31.1</version>
</dependency>
- java测试代码
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));
}
- 执行结果
plain
Alice
123
alice@example.com
性能测试
protobuf和json的性能对比
此处对比protobuf、hutool的json工具类、jackson的性能
- 引入依赖
xml
<!-- 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类
java
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;
}
}
- 测试代码
java
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);
}
}
运行结果
plain
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序列化和反序列化的性能。