Protocol Buffers 学习笔记

Protocol Buffers

Java 示例

目的

  1. 使用proto文件定义消息格式
  2. 使用protocol buffer编译器
  3. 使用protocol buffer API读写消息

示例说明

  • 一个简单的通讯录例子,可以从文件读写联系人信息。通讯录的每个人包含一个名字,一个ID,一个邮件地址和一个联系电话。
  • 对于这个需求,你有什么序列化的想法?下面列出一些解决方案:
    • 使用Java序列化。问题很多(可以看Effective Java这本书)且不支持多语言
    • 自己写序列化方式。需要自己解析和编码,适合简单数据
    • 序列化成XML。性能低,空间利用率低

定义protpcol文件

syntax = "proto2";

package tutorial;

option java_multiple_files = true;
option java_package = "com.example.tutorial.protos";
option java_outer_classname = "AddressBookProtos";

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}
  • 从java来看,就是定义一个AddressBook对象,AddressBook包含了一个集合类型的Person。

  • Person包含了4个字段:name,id,email,phones

  • phones是PhoneNumber引用类型的,PhoneNumber包含两个字段,PhoneType为枚举

  • 对应示例

package com.example.tutorial.protos;
public class AddressBookProtos{
  public static class Person{
    private String name;
    private int id;
    private String email;
    private PhoneNumber phones;
    public static class PhoneNumber{
      private String number;
      private PhoneType type=PhoneType.HOME;
      public static enum PhoneType{
        MOBILE,HOME,WORK;
      }
    }
  }
  public static class AddressBook{
    private List<Person> people;
  } 
}
// 省略了getter、setter等

proto文件说明

  • package用于声明包,可以作为java的包名,【必须有】多语言使用,解决定义冲突,可能不是java的反转域名规范;如果定义了java_package,使用java_package生成java包。
  • java_outer_classname用于定义外部类名称,不写会使用文件名驼峰形式。my_proto.proto会使用外部名MyProto。
  • java_multiple_files = true用于指示生成多个文件还是单个文件。
  • 基本类型 bool, int32, float, double, and string ,enum,嵌套messge
  • =1,=2这些是用于定义二进制编解码的字段顺序,必须唯一,1-15只占用一个字节,最好用来定义常用的字段和可重复字段
  • 每个字段有一个修饰符:required(必须)、optional(可选)、repeated(可重复)。

编译proto文件

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
  • 示例:protoc --java_out=. data.proto

protocol buffer API

  • 每个类都会生成POJO类和Buidler,Builder类有getter和setter,POJO类只有getter

写数据

import com.example.tutorial.protos.AddressBook;
import com.example.tutorial.protos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;

class AddPerson {
  // This function fills in a Person message based on user input.
  static Person PromptForAddress(BufferedReader stdin,
                                 PrintStream stdout) throws IOException {
    Person.Builder person = Person.newBuilder();

    stdout.print("Enter person ID: ");
    person.setId(Integer.valueOf(stdin.readLine()));

    stdout.print("Enter name: ");
    person.setName(stdin.readLine());

    stdout.print("Enter email address (blank for none): ");
    String email = stdin.readLine();
    if (email.length() > 0) {
      person.setEmail(email);
    }

    while (true) {
      stdout.print("Enter a phone number (or leave blank to finish): ");
      String number = stdin.readLine();
      if (number.length() == 0) {
        break;
      }

      Person.PhoneNumber.Builder phoneNumber =
        Person.PhoneNumber.newBuilder().setNumber(number);

      stdout.print("Is this a mobile, home, or work phone? ");
      String type = stdin.readLine();
      if (type.equals("mobile")) {
        phoneNumber.setType(Person.PhoneType.MOBILE);
      } else if (type.equals("home")) {
        phoneNumber.setType(Person.PhoneType.HOME);
      } else if (type.equals("work")) {
        phoneNumber.setType(Person.PhoneType.WORK);
      } else {
        stdout.println("Unknown phone type.  Using default.");
      }

      person.addPhones(phoneNumber);
    }

    return person.build();
  }

  // Main function:  Reads the entire address book from a file,
  //   adds one person based on user input, then writes it back out to the same
  //   file.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  AddPerson ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    AddressBook.Builder addressBook = AddressBook.newBuilder();

    // Read the existing address book.
    try {
      addressBook.mergeFrom(new FileInputStream(args[0]));
    } catch (FileNotFoundException e) {
      System.out.println(args[0] + ": File not found.  Creating a new file.");
    }

    // Add an address.
    addressBook.addPerson(
      PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
                       System.out));

    // Write the new address book back to disk.
    FileOutputStream output = new FileOutputStream(args[0]);
    addressBook.build().writeTo(output);
    output.close();
  }
}

读数据

import com.example.tutorial.protos.AddressBook;
import com.example.tutorial.protos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;

class ListPeople {
  // Iterates though all people in the AddressBook and prints info about them.
  static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPeopleList()) {
      System.out.println("Person ID: " + person.getId());
      System.out.println("  Name: " + person.getName());
      if (person.hasEmail()) {
        System.out.println("  E-mail address: " + person.getEmail());
      }

      for (Person.PhoneNumber phoneNumber : person.getPhonesList()) {
        switch (phoneNumber.getType()) {
          case MOBILE:
            System.out.print("  Mobile phone #: ");
            break;
          case HOME:
            System.out.print("  Home phone #: ");
            break;
          case WORK:
            System.out.print("  Work phone #: ");
            break;
        }
        System.out.println(phoneNumber.getNumber());
      }
    }
  }

  // Main function:  Reads the entire address book from a file and prints all
  //   the information inside.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    // Read the existing address book.
    AddressBook addressBook =
      AddressBook.parseFrom(new FileInputStream(args[0]));

    Print(addressBook);
  }
}

Guides

上一篇:《React Native移动开发实战》一一第3章 React Native的组件(1)


下一篇:用spring的 InitializingBean 的 afterPropertiesSet 来初始化