伯乐共勉

讨论。NET专区
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Mozilla研究—组件的创建过程

Posted on 2007-09-04 10:42  伯乐共勉  阅读(244)  评论(0编辑  收藏  举报

 

mozilla是一个以浏览器为中心的软件平台,它在我们平台中占有重要地位。我们用它来实现WEB浏览器、WAP浏览器、邮件系统、电子书和帮助阅读器等应用程序。为此,我最近花了不少时间去阅读mozilla的代码和文档,我将写一系列的BLOG作为笔记,供有需要的朋友参考。本文介绍一下组件的创建过程。

 

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

 

而在组件对象模型(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)必须对外exportNSGetModule函数,该函数的功能是用来得到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创建组件的实例,并保存该实例的引用以备后面再使用。

 

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