1. Обзор
В этой статье мы рассмотрим буфер протокола Google (protobuf) — широко известный формат двоичных данных, не зависящий от языка. Мы можем определить файл с протоколом, а затем, используя этот протокол, мы можем генерировать код на таких языках, как Java, C++, C#, Go или Python.
Это вводная статья к самому формату; если вы хотите узнать, как использовать формат с веб-приложением Spring, ознакомьтесь с этой статьей .
2. Определение зависимостей Maven
Чтобы использовать протокольные буферы Java, нам нужно добавить зависимость Maven к protobuf-java :
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<properties>
<protobuf.version>3.2.0</protobuf.version>
</properties>
3. Определение протокола
Начнем с примера. Мы можем определить очень простой протокол в формате protobuf:
message Person {
required string name = 1;
}
Это протокол простого сообщения типа Person
, который имеет только одно обязательное поле — имя, имеющее строковый
тип.
Давайте рассмотрим более сложный пример определения протокола. Допустим, нам нужно хранить информацию о человеке в формате protobuf:
пакет протобуф;
package protobuf;
option java_package = "com.foreach.protobuf";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
repeated string numbers = 4;
}
message AddressBook {
repeated Person people = 1;
}
Наш протокол состоит из двух типов данных: Person
и AddressBook.
После создания кода (подробнее об этом в следующем разделе) эти классы будут внутренними классами внутри класса AddressBookProtos
.
Когда мы хотим определить обязательное поле — это означает, что создание объекта без такого поля вызовет исключение
, нам нужно использовать обязательное
ключевое слово.
Создание поля с необязательным
ключевым словом означает, что это поле не нужно задавать. Повторяющееся ключевое
слово представляет собой тип массива переменного размера.
Все поля проиндексированы — поле, обозначенное номером 1, будет сохранено как первое поле в двоичном файле. Поле, отмеченное цифрой 2, будет сохранено следующим и так далее. Это дает нам лучший контроль над расположением полей в памяти.
4. Генерация кода Java из файла Protobuf
Определив файл, мы можем сгенерировать из него код.
Во-первых, нам нужно установить protobuf на нашу машину. Как только мы это сделаем, мы можем сгенерировать код, выполнив команду protoc
:
protoc -I=. --java_out=. addressbook.proto
Команда protoc
сгенерирует выходной файл Java из нашего файла addressbook.proto
.
Параметр -I
указывает каталог, в котором находится прото
- файл. java-out
указывает каталог, в котором будет создан сгенерированный класс. ``
Сгенерированный класс будет иметь сеттеры, геттеры, конструкторы и построители для наших определенных сообщений. Он также будет иметь несколько полезных методов для сохранения файлов protobuf и их десериализации из двоичного формата в класс Java.
5. Создание экземпляра сообщений, определенных Protobuf
Мы можем легко использовать сгенерированный код для создания Java-экземпляра класса Person
:
String email = "j@foreach.com";
int id = new Random().nextInt();
String name = "Michael Program";
String number = "01234567890";
AddressBookProtos.Person person =
AddressBookProtos.Person.newBuilder()
.setId(id)
.setName(name)
.setEmail(email)
.addNumbers(number)
.build();
assertEquals(person.getEmail(), email);
assertEquals(person.getId(), id);
assertEquals(person.getName(), name);
assertEquals(person.getNumbers(0), number);
Мы можем создать свободный построитель, используя метод newBuilder()
для нужного типа сообщения. После настройки всех необходимых полей мы можем вызвать метод build()
для создания экземпляра класса Person
.
6. Сериализация и десериализация Protobuf
Создав экземпляр класса Person
, мы хотим сохранить его на диске в двоичном формате, совместимом с созданным протоколом. Допустим, мы хотим создать экземпляр класса AddressBook
и добавить в этот объект одного человека.
Затем мы хотим сохранить этот файл на диске — в автоматически сгенерированном коде есть вспомогательный метод writeTo() , который мы можем использовать:
AddressBookProtos.AddressBook addressBook
= AddressBookProtos.AddressBook.newBuilder().addPeople(person).build();
FileOutputStream fos = new FileOutputStream(filePath);
addressBook.writeTo(fos);
После выполнения этого метода наш объект будет сериализован в двоичный формат и сохранен на диске. Чтобы загрузить эти данные с диска и десериализовать их обратно в объект AddressBook
, мы можем использовать метод mergeFrom()
:
AddressBookProtos.AddressBook deserialized
= AddressBookProtos.AddressBook.newBuilder()
.mergeFrom(new FileInputStream(filePath)).build();
assertEquals(deserialized.getPeople(0).getEmail(), email);
assertEquals(deserialized.getPeople(0).getId(), id);
assertEquals(deserialized.getPeople(0).getName(), name);
assertEquals(deserialized.getPeople(0).getNumbers(0), number);
7. Заключение
В этой быстрой статье мы представили стандарт для описания и хранения данных в двоичном формате — Google Protocol Buffer.
Мы создали простой протокол, создали экземпляр Java, который соответствует определенному протоколу. Далее мы увидели, как сериализовать и десериализовать объекты с помощью protobuf.
Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub — это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.