我们先看一下JAVA反射的概念:
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!
说的很学术,看的也有点晕。 说白了就是,在运行时根据类的名字(字符串),创建类实例的能力。
当然还包含类的一些内部成员的信息,这样创建出来的实例就可以动态的调用类的方法以及成员。
JAVA在语言层面,提供了对Reflection的支持,当然还包括一些解释型语言也提供了反射支持,因为动态解释行语言支持起来是相对容易的。
C++ 在语言本身并没有支持反射机制,但是protobuf通过维护一些内部数据结构,提供了对proto文件生成的file, message, filed数据结构提供了反射的支持。
这对于远程调用,的类型识别作用非常大。试想一下,你只需要把message类型的字符串,和序列化的二禁止数据,传输给远端,远端服务器就可以根据message类型
构建类型对象,进而反序列化,还原本端的对象实例。应用程序,在也不用维护类型ID到对象实例的对应关系,这是非常方便的。
那么protobuf是怎么实现这种反射机制呢。下面我会从源码的角度分析一下。这里我们会从结果倒推回去的逆向逻辑。
1, 所有的生成的集成于message类型的子类,都实现了prototype 模式(支持从一个对象的实例,构造出另一个队像)。
2, message的子类都有它本身的静态对象, 这样如果找到这个类静态对象,则可以使用这个对象new 一个新的对象实例。
class Person : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:tutorial.Person) */ {
public:
Person();
virtual ~Person();
Person(const Person& from);
inline Person& operator=(const Person& from) {
CopyFrom(from);
return *this;
}
static const ::google::protobuf::Descriptor* descriptor();
static const Person& default_instance(); //一个今天的Person对象, 他的作用就是调用NEW生成新的对象。
void Swap(Person* other);
inline Person* New() const { return New(NULL); } //New 会生成一个新的对象的指针。
Person* New(::google::protobuf::Arena* arena) const;
const ::google::protobuf::Descriptor* Person_descriptor_ = NULL; //Person 的 Descriptor
3,message类的Descriptor会跟 default_instance,建立一个映射,这样就能通过Descriptor找到default_instance
这个映射关系的建立,引入了另外一个中间的类。
void protobuf_RegisterTypes(const ::std::string&) {
protobuf_AssignDescriptorsOnce();
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
Person_descriptor_, &Person::default_instance()); //这里MessageFactory会注册Descriptor到default_instance的映射关系。
4, 那接下来要做什么呢,当然是怎样找到一个message的Descriptor.
class LIBPROTOBUF_EXPORT DescriptorPool {
public:
// Create a normal, empty DescriptorPool.
DescriptorPool();
const Descriptor* FindMessageTypeByName(const string& name) const; //DescriptorPool是一个全局对象, 他提供了根据字符串找到 Descriptor的机制。
const FieldDescriptor* FindFieldByName(const string& name) const;
const FieldDescriptor* FindExtensionByName(const string& name) const;
const OneofDescriptor* FindOneofByName(const string& name) const;
const EnumDescriptor* FindEnumTypeByName(const string& name) const;
const EnumValueDescriptor* FindEnumValueByName(const string& name) const;
const ServiceDescriptor* FindServiceByName(const string& name) const;
const MethodDescriptor* FindMethodByName(const string& name) const;
//第一种方式:
Classc1 = Class.forName("Employee");
//第二种方式:
//java中每个类型都有class 属性.
Classc2 = Employee.class;
//第三种方式:
//java语言中任何一个java对象都有getClass 方法
Employee e = new Employee();
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!
说的很学术,看的也有点晕。 说白了就是,在运行时根据类的名字(字符串),创建类实例的能力。
当然还包含类的一些内部成员的信息,这样创建出来的实例就可以动态的调用类的方法以及成员。
JAVA在语言层面,提供了对Reflection的支持,当然还包括一些解释型语言也提供了反射支持,因为动态解释行语言支持起来是相对容易的。
C++ 在语言本身并没有支持反射机制,但是protobuf通过维护一些内部数据结构,提供了对proto文件生成的file, message, filed数据结构提供了反射的支持。
这对于远程调用,的类型识别作用非常大。试想一下,你只需要把message类型的字符串,和序列化的二禁止数据,传输给远端,远端服务器就可以根据message类型
构建类型对象,进而反序列化,还原本端的对象实例。应用程序,在也不用维护类型ID到对象实例的对应关系,这是非常方便的。
那么protobuf是怎么实现这种反射机制呢。下面我会从源码的角度分析一下。这里我们会从结果倒推回去的逆向逻辑。
1, 所有的生成的集成于message类型的子类,都实现了prototype 模式(支持从一个对象的实例,构造出另一个队像)。
2, message的子类都有它本身的静态对象, 这样如果找到这个类静态对象,则可以使用这个对象new 一个新的对象实例。
class Person : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:tutorial.Person) */ {
public:
Person();
virtual ~Person();
Person(const Person& from);
inline Person& operator=(const Person& from) {
CopyFrom(from);
return *this;
}
static const ::google::protobuf::Descriptor* descriptor();
static const Person& default_instance(); //一个今天的Person对象, 他的作用就是调用NEW生成新的对象。
void Swap(Person* other);
inline Person* New() const { return New(NULL); } //New 会生成一个新的对象的指针。
Person* New(::google::protobuf::Arena* arena) const;
const ::google::protobuf::Descriptor* Person_descriptor_ = NULL; //Person 的 Descriptor
3,message类的Descriptor会跟 default_instance,建立一个映射,这样就能通过Descriptor找到default_instance
这个映射关系的建立,引入了另外一个中间的类。
void protobuf_RegisterTypes(const ::std::string&) {
protobuf_AssignDescriptorsOnce();
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
Person_descriptor_, &Person::default_instance()); //这里MessageFactory会注册Descriptor到default_instance的映射关系。
4, 那接下来要做什么呢,当然是怎样找到一个message的Descriptor.
class LIBPROTOBUF_EXPORT DescriptorPool {
public:
// Create a normal, empty DescriptorPool.
DescriptorPool();
const Descriptor* FindMessageTypeByName(const string& name) const; //DescriptorPool是一个全局对象, 他提供了根据字符串找到 Descriptor的机制。
const FieldDescriptor* FindFieldByName(const string& name) const;
const FieldDescriptor* FindExtensionByName(const string& name) const;
const OneofDescriptor* FindOneofByName(const string& name) const;
const EnumDescriptor* FindEnumTypeByName(const string& name) const;
const EnumValueDescriptor* FindEnumValueByName(const string& name) const;
const ServiceDescriptor* FindServiceByName(const string& name) const;
const MethodDescriptor* FindMethodByName(const string& name) const;
//第一种方式:
Classc1 = Class.forName("Employee");
//第二种方式:
//java中每个类型都有class 属性.
Classc2 = Employee.class;
//第三种方式:
//java语言中任何一个java对象都有getClass 方法
Employee e = new Employee();
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)