該篇Blog和上一篇(C++實例)基本相同,只是面向於咱們團隊中的Java工程師,畢竟咱們項目的前端部分是基於Android開發的,並且咱們研發團隊中目前主要使用的開發語言就是C++、Java和Python,其中Python主要用於編寫各類工具程序。然而爲了保證該篇Blog的完整性和獨立性,我仍然會將上一篇Blog中已經出現的內容再一次贅述,同時對於Java中特有的部分也會着重介紹。
1、生成目標語言代碼。
下面的命令幫助咱們將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。
2、簡單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_package和java_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 } 85 //克隆出一個Builder對象。 86