`
阿尔萨斯
  • 浏览: 4185386 次
社区版块
存档分类
最新评论

Mozilla研究-组件的创建过程

 
阅读更多

组件的创建过程

在《设计模式》一书中,作者把创建模式放在了该书的最前面,我想这一定是经过深思熟虑之后才决定的。试想,如果没有创建模式,接口的使用者必须要知道接口的具体实现,才能创建并使用它,那么接口的实现可能就要遍布整个系统了。如果是这样,那还能算是分离了接口与实现吗,还能算是针对接口编程吗?答案是否定的,所以创建模式尽管很简单,但它却是针对接口编程必要的手段。

而在组件对象模型(COM) 中大量使用了创建模式中的工厂模式。在XPCOM中,几乎所有组件都是通过工厂(Factory)来创建的。一个组件可以实现多个接口,一个 Factory可以创建多个接口,所以通常一个组件只要实现一个Factory接口就够了,而不必为每个接口实现一个Factory。

MSCOM的Factory接口叫作IClassFactory。记得当时有人说,这个名字取得不好。原因是IClassFactory生产的是对象而不是类,所以正确的名字应该叫IObjectFactory。呵,这倒是很有道理的。

在介绍mozilla的组件创建过程之前,让我们先回忆一下MSCOM组件的创建过程,这对理解XPCOM组件的创建过程也是有帮助的。

对于以动态库形式提供的MSCOM组件,必须export出下面这些函数:

1. DllGetClassObject 用来查询组件内的接口。

2. DllRegisterServer 向系统注册组件。

3. DllUnregisterServer 向系统注销组件。

4. DllCanUnloadNow 判断是否可以安全卸载组件。

为了让客户可以使用自己,组件首先要把自己注册到系统中去。注册过程实际上就是向注册表中写入一些信息,让客户可以找到组件对应的动态库。然后客户通过系统API (如CoCreateInstance)创建来组件的实例,而CoCreateInstance等API先从注册表找到该组件的动态库,再调用动态库 DllGetClassObject函数查询到IClassFactory接口,最后通过IClassFactory去创建组件的实例。

在mozilla中,XPCOM组件的创建与MSCOM组件的创建类似。这里的模块(Module)是一个物理实体(通常是动态库或JS包),一个模块(Module)内可以封装多个组件,而每个组件内又可以实现多个接口。

模块(Module)必须对外export出NSGetModule函数,该函数的功能是用来得到nsIModule接口的。nsIModule接口的主要函数如下:

1. GetClassObject用来查询组件内的接口。对应MSCOM的DllGetClassObject函数。

2. RegisterSelf向系统注册组件。对应MSCOM的DllRegisterServer函数。

3. UnregisterSelf向系统注销组件。对应MSCOM的DllUnregisterServer函数。

4. CanUnload判断是否可以安全卸载组件。对应MSCOM的DllCanUnloadNow函数。

mozilla 实现了一套宏,一个通用的Module(nsGenericModule)和一个通用的Factory(nsGenericFactory),在它们的帮助下,模块只要实现一个nsModuleComponentInfo结构就行了,这大大简化了开发的繁杂度。nsModuleComponentInfo 的成员仍然比较多,幸好通常只要提供mDescription、mCID、mContractID和mConstructor几个成员就行了。

nsGenericFactory 实现了nsIFactory接口,它只是对nsModuleComponentInfo的一个包装。NsIFactory的功能和MSCOM中的IClassFactory差不多,它需要提供下列接口函数:

1. CreateInstance 创建指定接口的实例。

2. LockFactory 加锁/解锁工厂。

有了以上的背景知识,mozilla的组件创建过程就不难理解了:

1. mozilla调用NSGetModule获取IModule接口。(初始化时)

2. mozilla调用IModule接口的RegisterSelf函数,向组件管理器注册组件。(初始化时)

3. 通过组件管理器查找组件的nsIFactory接口,然后通过组件的nsIFactory接口创建组件。

这样一来,组件的使用者和实现者之间耦合就降到最低了,两者独立变化而互不影响。

值得注意的是,在XPCOM中,组件的创建有两种方式:

1. 创建实例(CreateInstance)。这是普通的创建方式,每次都调用Factory::CreateInstance来创建新的实例。

2. 获取服务(GetService)。服务(Service)一词容易让人误解(我开始以为是跨进程的调用呢),其实这里的服务就是一个单件,也就是说该组件只有一个实例存在。获取服务时,组件管理器发现如果先前已经创建过该组件的实例,就直接返回先前的实例,否则调用 Factory::CreateInstance创建组件的实例,并保存该实例的引用以备后面再使用。

无论是以创建实例还是以获取服务的方式创建,对组件本身的实现没有太大的影响。只是如果按获取服务的方式创建,而且该服务可能在多线程环境下使用,那么组件要自己实现加锁保护。

分享到:
评论
Global site tag (gtag.js) - Google Analytics