反射機制
java在運行狀態時,能夠知道任意類的所有屬性和方法,都能夠調用任意對象的任意方法和屬性。這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
C++本身沒有反射機制。protobuf通過proto文件生成相應的message和service,protobuf也通過proto文件提供反射機制,程序在運行時可以通過proto獲取任意message和任意service的屬性和方法,也可以在運行時調用message的屬性和方法。
獲取message和service的屬性和方法
protobuf通過Descriptor獲取任意message或service的屬性和方法,Descriptor主要包括了以下集中類型:
FileDescriptor 獲取proto文件中的Descriptor和ServiceDescriptor
Descriptor 獲取類message屬性和方法,包括FieldDescriptor 和 EnumDescriptor等
FieldDescriptor 獲取message中各個字段的類型、標簽、名稱等
EnumDescriptor 獲取Enum中的各個字段名稱、值等
ServiceDescriptor 獲取service中的MethodDescriptor
MethodDescriptor 獲取各個rpc中的request、response、名稱等
當我們獲得proto文件的FileDescriptor時,我們就可以獲得所有的service的Descriptor和service的ServiceDescriptor,進而獲取其相應的字段或rpc。也就是說,如果能獲取到proto文件的FileDescriptor,就能所有的proto文件中的所有內容。
那么如何獲取proto文件的FileDescriptor呢?protobuf對應以下兩種不同的情況提供了相應的辦法
1、 使用protoc將proto文件並生成相應的.h和.cpp文件。
這中情況下protobuf已經解析好proto文件,並將所有的Descriptor放在DescriptorPool中了。,可以根據proto的文件名,通過DescriptorPool獲取到相應的FileDescriptor,例如,現有test.proto文件,那么可以通過DescriptorPool::generated_pool()獲取到其FileDescriptor。其實,對於任意的message和service,也都可以根據其名稱,通過DescriptorPool獲取相應的Descriptor和ServiceDescriptor。例如:
2、 只有proto文件,不使用protoc。
這種情況需要去手動解析proto文件,然后再獲取FileDescriptor。還好protobuf提供了相應的解析器compiler,通過compiler可以很方便得獲取proto文件的FileDescriptor,具體如下:
調用message的屬性和方法
想要調用message的屬性和方法,就得先獲取相應的message對象。protobuf的message對象都是放在MessageFactory中的,可以通過Descriptor,具體如下:
有了message對象,並不能直接調用其對象和方法,因為所有的message對象都是Message*類型的,但不同的message對象的屬性和方法是不一樣的,在這里,Message*只是指向相應的message大小的地址空間,並不知道對應的message中到底有哪些屬性和方法。
protobuf是通過Reflection調用message的屬性和方法的。message中的方法只有對各個屬性的get和set,而調用message的屬性其實也就是調用屬性的get。調用message的某一個屬性的get,就需要該屬性的Descriptor,通過Reflection獲取message獲取相應的值;調用message某一屬性的set,也需要該屬性的Descriptor,通過Reflection將相應的值寫入到message相應的屬性。例如:
反射機制的應用
有了反射機制,可以寫很多工具,比如:基於pb的自動化測試工具、pb轉json或xml的工具、pb直接寫到數據庫的工具等。反射只是一種機制,有什么樣的應用場景需要你的想象力!