Цей туторіал демонструє використання Protocol Buffers в Java. В процесі створення прикладу простої програми буде розглянуто наступне:
Визначення форматів повідомлення у .proto файлі.
Використовування Protocol Buffer компілятора.
Застосовування Java Protocol Buffer API при написанні та читанні повідомлень.
Більш детальніший опис використання доступний у: , , та .
Де Шукати Приклад Коду
Приклад коду включено у пакет вихідного коду під «examples» каталогом. .
Визначення Необхідного Формату Протоколу
Для створення програми «Адресна книга» потрібно розпочати з .proto файлу. Визначення у .proto файлі є простими: додавання повідомлення (message) до кожної структури даних для серіалізації, потім опис ім'я і типу для кожного поля в повідомленні. Ось .proto файл для опису необхідних повідомлень — addressbook.proto:
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
Легко помітити, що синтаксис схожий на С++ або Java. Давайте пройдемось по кожній частині файлу і побачимо для чого вона.
.proto файл починається з опису пакету, що допомагає запобігти конфліктові імен між різними проектами. В Java ім'я пакету використовується як Java пакет, якщо явно не вказано java_package, як наведено у прикладі. Навіть якщо забезпечити java_package, всерівно необхідно визначити звичайний пакет, щоб запобігти конфліктові імен у Protocol Buffers просторі імен (name space), а також не пов'язаних з Java мовами.
Після оголошення пакету можна побачити дві опції, які є специфічними для Java: java_package та java_outer_classname. java_package деталізує в якому Java пакеті повинні міститись згенеровані класи. Якщо не уточнити це явно, то він просто збігається з назвою пакета даного описом пакету, проте ці імена, як правило, не відповідають назвам Java пакетів (оскільки вони, зазвичай, не починаються з імені домену). Опція java_outer_classname визначає ім'я класу, який повинен містити всі класи в цьому файлі. Якщо не вказати java_outer_classname у явному вигляді, то назва буде сформована шляхом перетворення імені файлу в камель регістру (camel case). Наприклад, «my_proto.proto» буде, за замовчуванням, використовувати «MyProto» в якості імені зовнішнього класу.
Наступним є визначення повідомлення — сукупність, яка містить тільки набір типізованих полів. Велика кількість стандартних простих типів даних є доступною для типів полів, у тому числі bool, int32, float, double, та string. Також можна додати до повідомлення подальші структури, використовуючи інакші типи повідомлення як типи полів — в наведеному вище прикладі повідомлення Person містить повідомлення PhoneNumber, у той час як повідомлення AddressBook включає в себе повідомлення Person. Можна навіть визначити типи повідомлення, вкладених всередину інших повідомлень — з прикладу: тип PhoneNumber є описаний в Person. Також можливо описувати нумеровані типи, якщо потрібно, щоб одне з полів включало один із встановлених списоків змінних — тут потрібно зазначити, що телефон може бути одним з MOBILE, HOME чи WORK.
"= 1", "= 2" мітки кожному елементові присвоюють унікальний «теґ» («tag»), який використовується полем у двійковому кодуванні. Для номерів теґів 1—15 при кодуванні необхідно на один байт менше, ніж для великих чисел, задля оптимізації потрібно використовувати теґи для широко використовуваних або повторюваних елементів, залишаючи 16 і вище мітки для менш поширеного використання необов'язкових елементів. Необхідно перекодовувати число теґу для кожного елемента у повторюваному полі, таким чином повторювані поля є гарними претендентами для цієї оптимізації.
Кожне поле слід представляти з одним із наступних модифікаторів:
Обов'язкове: значення поля повинне бути визначеним, інакше повідомлення вважатиметься не ініціалізованим («uninitialized»). Спроба побудувати неініціалізірованние повідомлення видасть RuntimeException. Аналіз неініціалізірованние повідомлення видасть IOException. Крім цього, поведінка необхідного поля така ж, як і необов'язкового поля.
Вибіркове: поле може, або не може бути визначеним. У випадку невизначеного поля використовується значення за замовчуванням. Для простих типів можна уточнювати стандартне значення, як показано у прикладі для типу телефонного номера. У протилежному випадку, використовується стандартне системне: нуль для числових типів, порожня стрічка для string, false для bool. Для вкладених повідомлень стандартне значення є завжди «типовим екземпляром» («default instance») або «прообразом» («prototype») повідомлення, яке не має визначених полів. Виклик accessor-а для отримання значення необов'язкового (або ж необхідного) поля, яке не було чітко визначене, завжди вертає його значення по замовчанню.
Повторно: поле може повторюватися будь-яку кількість разів (або не повторюватися). Порядок повторюваних значеннь зберігатиметься в protocol buffer. Подумайте про повторювані поля як про масиви динамічно розміру.
Обов'язковий є постійно Потрібно бути дуже обережнім позначаючи поле обов'язковим. Якщо в певний момент слід зупинити записування або відсилання обов'язкового поля, то буде проблематично змінити його на необов'язкове поле, старі читальні сервіси розглядатимуть повідомлення без цього поля як незавершені та можуть ненавмисно відхилити або викинути їх. Натомість потрібно враховувати написання програмно специфічних нетипових алгоритмів перевірки для даних buffer-ів. Деякі інженери, що працюють на Google, дійшли до висновку, що використання обов'язкових приносить більше збитків, ніж користі; вони надають перевагу тільки необов'язковим та повторюваним. Однак їхнє бачення не є універсальним.
Докладнішу інформацію про написання .proto файлу, включно з всеможливими типами полів, можна знайти на . Не слід шукати аналогічну здатність у наслідуванні класу, незважаючи на те, що protocol buffer-и так не поводяться.
Компіляція Protocol Buffers
Тепер, коли вже є .proto, наступним буде генерування класів необхідних для зчитування та написання AddressBook (а звідси Person і PhoneNumber) повідомлення. Для цього потрібно запустити компілятор protocol buffer-у protoc для даного .proto:
1. Якщо компілятор не встановлений, то слід та слідувати інструкціям з README.
2. Тепер потрібно запустити компілятор, вибравши початковий каталог (де занаходяться вихідний коду програми — якщо не ввести значення, то використовується поточний каталог), каталог адресату (де повинен знаходитися згенерований код; часто так як $SRC_DIR) та шлях до .proto файлу. У цьому випадку:
Оскільки необхідно використовувати Java класи, то включено опцію --java_out — аналогічна можливість передбачена для підтримки інших мов.
За допомогою цього генерується com/example/tutorial/AddressBookProtos.java у визначений каталог адресату.
Protocol Buffer API
Розглядаючи деякий згенерований код можна побачити які саме класи та методи були створені компілятором. AddressBookProtos.java описує клас AddressBookProtos, всередині якого є вкладений клас для кожного визначеного повідомлення в addressbook.proto. Кожен клас має свій власний конструктор (Builder), що використовується для створення екземплярів цього ж класу. Дізнатись більше про конструктори можна у розділом нижче.
Обидва повідомлення та конструктори мають автоматично згенеровані accessor методи для кожного поля повідомлення; повідомлення містить тільки getter-и доти, доки конструктори мають обидва getter-и та setter-и. Ось деякі з accessor-ів для класу Person (реалізація пропущена задля стислості):
// required string name = 1;
public boolean hasName();
public String getName();
// required int32 id = 2;
public boolean hasId();
public int getId();
// optional string email = 3;
public boolean hasEmail();
public String getEmail();
// repeated .tutorial.Person.PhoneNumber phone = 4;
public List<PhoneNumber> getPhoneList();
public int getPhoneCount();
public PhoneNumber getPhone(int index);
Тим часом, Person.Builder містить ті самі getter-и, а також setter-и:
// required string name = 1;
public boolean hasName();
public java.lang.String getName();
public Builder setName(String value);
public Builder clearName();
// required int32 id = 2;
public boolean hasId();
public int getId();
public Builder setId(int value);
public Builder clearId();
// optional string email = 3;
public boolean hasEmail();
public String getEmail();
public Builder setEmail(String value);
public Builder clearEmail();
// repeated .tutorial.Person.PhoneNumber phone = 4;
public List<PhoneNumber> getPhoneList();
public int getPhoneCount();
public PhoneNumber getPhone(int index);
public Builder setPhone(int index, PhoneNumber value);
public Builder addPhone(PhoneNumber value);
public Builder addAllPhone(Iterable<PhoneNumber> value);
public Builder clearPhone();
Помітно, що для кожного поля є прості JavaBeans-стилізовані геттери та сеттери. Hарешті, кожне поле має clear метод, який обнуляє назад поля для свого порожнього стану.
Повторювані поля містять деякі додаткові методи — Count метод (який лише визначає розмір списку); геттери та сеттери, які одержують або встановлюють конкретний елемент зі списку за індексом; add метод, котрий добавляє новий елемент у список; а також addAll метод, що додає до списку весь контейнер, наповнений елементами.
Слід звернути увагу, як ці аксессор методи використовують присвоювання імен камель регістру (camel-case naming), навіть незважаючи на те, що .proto файл містить нижній регістр із символом підкреслювання. Це перетворення здійснюється автоматично за допомогою protocol buffer компілятора так, що згенеровані класи відповідають стандартам конвенції Java стилів. Слід завжди використовувати нижній регістр із символом підкреслювання для назв полів в даному .proto файлі; це забезпечує хорошу практику в присвоюванні імен для будь-яких згенерованих мов. Для кращого стилю .proto див. .
Більш докладну інформацію про те, які компоненти генеруються protocol компілятором для любого докладного визначення поля, див. .
Коментарі (0)
RSS згорнути / розгорнутиТільки зареєстровані й авторизовані користувачі можуть залишати коментарі.