Protocol Buffer技术详解(Java实例)

   该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发团队中目前主要使用的开发语言就是C++、Java和Python,其中Python主要用于编写各种工具程序。然而为了保证该篇Blog的完整性和独立性,我仍然会将上一篇Blog中已经出现的内容再一次赘述,同时对于Java中特有的部分也会着重介绍。
    
      一、生成目标语言代码。
      下面的命令帮助我们将MyMessage.proto文件中定义的一组Protocol Buffer格式的消息编译成目标语言(Java)的代码。至于消息的内容,我们会在后面以分段的形式逐一列出,同时也会在附件中给出所有源代码。
      protoc -I=./message --java_out=./src ./MyMessage.proto
      从上面的命令行参数中可以看出,待编译的文件为MyMessage.proto,他存放在当前目录的message子目录下。--java_out参数则指示编译工具我们需要生成目标语言是java,输出目录是当前目录的src子目录。这里需要补充说明的是,因为在MyMessage.proto文件中定义了option java_package = "com.lsk.lyphone"的文件级选项,所以输出的目前是src/com/lsk/lyphone,生成的目标代码文件名是MyMessage.java。
    
      二、简单message生成的Java代码。
      这里先定义一个最简单的message,其中只是包含原始类型的字段。
      option java_package = "com.lsk.lyphone";
      option java_outer_classname = "LYPhoneMessage";
      option optimize_for = LITE_RUNTIME;
      message LogonReqMessage {
          required int64 acctID = 1;
          required string passwd = 2;
      }
      对于选项java_packagejava_outer_classname的功能,我们已经在之前的一篇Blog(语言规范)中进行了清晰的阐述,这里就不在另行介绍了。然而对于选项optimize_for,这里将结合本例给出一些实用性描述。
      当前.proto文件中该选项的值为LITE_RUNTIME,因此由该.proto文件生成的所有Java类的父类均为com.google.protobuf.GeneratedMessageLite,而非com.google.protobuf.GeneratedMessage,同时与之对应的Builder类则均继承自com.google.protobuf.MessageLiteOrBuilder,而非com.google.protobuf.MessageOrBuilder。在之前的博客中已经给出了一些简要的说明,MessageLite接口是Message的父接口,在MessageLite中将缺少Protocol Buffer对反射的支持,而此功能均在Message接口中提供了接口规范,同时又在其实现类GeneratedMessage中给予了最小功能的实现。对于我们的项目而言,整个系统相对比较封闭,不会和更多的外部程序进行交互,与此同时,我们的客户端部分又是运行在Android平台,有鉴于此,我们考虑使用LITE版本的Protocol Buffer。这样不仅可以得到更高编码效率,而且生成代码编译后所占用的资源也会更少,至于反射所能带来的灵活性和极易扩展性,对于该项目而言完全可以忽略。下面我们来看一下由message LogonReqMessage生成的Java类的部分声明,以及常用方法的说明性注释。
      在做各种case的对比性分析之前必须要事先声明的是,Protocol Buffer针对Java语言所生成的代码和C++相比存在一个非常重要的差别,即为每个消息均会生成一个Builder接口和一个与消息对应的实现类,该实现类又将同时实现生成的Builder接口和扩展Protocol Buffer内置的GeneratedMessageLite(或GeneratedMessage)类。这一点对于Protocol Buffer而言,是巧妙的使用了设计模式中的Builder模式。换言之,对于所有消息字段的修改操作均需要通过与其对应的Builder接口辅助完成。相信我们会通过对下面用例的学习可以得到更为清楚的认识。

复制代码
  1     //用于修改LogonReqMessage消息字段的辅助Builder接口。
  2     //该接口会为消息中的每个字段均提供getter和setter方法。  3 public interface LogonReqMessageOrBuilder  4 extends com.google.protobuf.MessageLiteOrBuilder {  5  6 // required int64 acctID = 1;  7 boolean hasAcctID();  8 long getAcctID();  9  10 // required string passwd = 2;  11 boolean hasPasswd();  12  String getPasswd();  13  }  14 //该类为final类,即不可以在被子类化了。这一点在Protocol Buffer的官方文档中给予了明确  15 //的说明,因为子类化将会破坏序列化和反序列化的过程。  16 public static final class LogonReqMessage extends  17  com.google.protobuf.GeneratedMessageLite  18 implements LogonReqMessageOrBuilder {  19  20 // Use LogonReqMessage.newBuilder() to construct.  21 // 由于所有构造函数均为私有方法,由此可见,我们不能直接new LogonReqMessage的对象  22 // 实例,而是只能通过与其对应Builder来构造,或是直接通过反序列化的方式生成。  23 private LogonReqMessage(Builder builder) {  24 super(builder);  25  }  26 //该静态方法为该类Builder接口的工厂方法。返回的Builder实现类在完成各个字段的  27 //初始化后,通过build()方法返回与其对应的消息实现类,即LogonReqMessage。  28 public static Builder newBuilder() { return Builder.create(); }  29 //通过该类的对象获取与其对应的Builder类对象,一般用于通过Builder类完成消息字段的修改。  30 public Builder toBuilder() { return newBuilder(this); }  31  32 private LogonReqMessage(boolean noInit) {}  33 //判断当前对象的所有字段是否都已经被初始化。  34 public final boolean isInitialized() {  35  ... ...  36  }  37 //获取已经被初始化后的对象序列化时所占用的字节空间。  38 public int getSerializedSize() {  39  ... ...  40  }  41 //从内存中饭序列化LogonReqMessage对象。  42 //Protocol Buffer中还提供其他一些接口方法,用于从不同的数据源反序列化对象。  43 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(byte[] data)  44 throws com.google.protobuf.InvalidProtocolBufferException {  45 return newBuilder().mergeFrom(data).buildParsed();  46  }  47 //功能和上一个函数相同,只是输入源改为InputStream接口。  48 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(java.io.InputStream input)  49 throws java.io.IOException {  50 return newBuilder().mergeFrom(input).buildParsed();  51  }  52  53 // required int64 acctID = 1;  54 // 下面的静态变量对应于该字段在.proto中定义的标签号。该变量的命名规则为:字段(全部大写) + _FIELD_NUMBER。  55 public static final int ACCTID_FIELD_NUMBER = 1;  56 public boolean hasAcctID() {  57 return ((bitField0_ & 0x00000001) == 0x00000001);  58  }  59 public long getAcctID() {  60 return acctID_;  61  }  62  63 // required string passwd = 2;  64 public static final int PASSWD_FIELD_NUMBER = 2;  65 public boolean hasPasswd() {  66 return ((bitField0_ & 0x00000002) == 0x00000002);  67  }  68 public String getPasswd() {  69  ... ...  70  }  71 //每一个Message类都会包含一个静态内部类,即与之对应的Builder类。上面代码中所涉及的Builder类均为该内部类。  72 public static final class Builder extends  73 com.google.protobuf.GeneratedMessageLite.Builder<  74 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage, Builder>  75 implements com.lsk.lyphone.LYPhoneMessage.LogonReqMessageOrBuilder {  76 //清空当前对象中的所有设置。调用该函数之后,本例中的hasAcctID和hasPasswd都会返回false。  77 public Builder clear() {  78 super.clear();  79 acctID_ = 0L;  80 bitField0_ = (bitField0_ & ~0x00000001);  81 passwd_ = "";  82 bitField0_ = (bitField0_ & ~0x00000002);  83 return this;  84  } 

转载于:https://www.cnblogs.com/For-her/p/3919606.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值