反射机制
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直接写到数据库的工具等。反射只是一种机制,有什么样的应用场景需要你的想象力!