BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

OSGi中的服务模型与扩展者模型

| 作者 张卫滨 关注  他的粉丝 发布于 2013年11月27日. 估计阅读时间: 8 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

在OSGi中,实现bundle间交互和扩展性有两种常见的方式,也就是服务模型(service model)和扩展者模型(extender model)。

服务模型较为容易理解,在这个模型中包含了服务的提供者、使用者以及注册中心。服务接口构成了提供者和使用者之间的契约,提供者将希望别人调用的服务发布到注册中心,使用者到注册中心查找符合需求的服务。在传统的服务模型中,都是通过查找的方式使用服务,但是随着Declarative Service以及Blueprint的流行,服务可以在运行时注入,简化了开发人员的编码过程。

图1:OSGi中的服务模型(图片来源于《OSGi实战》)

扩展者模式更为灵活和抽象,它是与OSGi的生命周期相关联的。在《OSGi实战》一书中是这样描述扩展者模式的:扩展者模式背后的主要思想是,在其他bundle的生命周期事件(安装、解析、启动、停止等)上构建动态扩展性。通常,应用中的某个bundle扮演扩展者的角色:它负责监听bundle的启动、停止。当一个bundle启动时,扩展者会对其进行检测并判断它是否是一个扩展bundle。扩展者检查bundle的清单文件(使用bundle.getHeaders())或bundle的内容(使用bundle.getEntry()),以此来寻找它能够识别的特定元数据。如果这个bundle确实包含一个扩展,那么该扩展会通过元数据来描述。扩展者读取元数据并执行必要的任务,这有助于将扩展bundle集成到应用中。如果扩展bundle被停止,扩展者同样会监听到,此时扩展者会将相关的扩展从应用中移除。(《OSGi实战》一书由人民邮电出版社翻译出版,对应的文字来源于第89页)。在Eclipse插件开发中最常用的扩展点模型就是一种典型的扩展者模式,不过这是Equinox的特有实现,并不是OSGi规范的一部分。

图2:《OSGi实战》一书中图形样例所对应的扩展者模式

近日,针对这两种模型的优劣和使用场景,有人阅读在完Apache Felix样例文档后产生了疑问,并在Stackoverflow进行了咨询。OSGi领域中的资深专家、Bndtools作者Neil Bartlett对此进行了细致的回复,而OSGi联盟技术总监 Peter Kriens更是专门撰写博文来解释这一问题。

Neil Bartlett认为服务更为常用,应该作为第一选择,除非有明确的理由才要设计新的扩展者。扩展者模式更为灵活和抽象,但是理解和使用起来会更加复杂。他以在OSGi应用中实现帮助系统作为例子,其他的bundle只需在约定的目录下包含一个HTML文档,核心的帮助系统bundle会扫描这些bundle以查找HTML文档并将其添加到主页的帮助索引中。这个特性如果使用服务来实现的话就会比较繁琐:假设遵循“白板(whiteboard)”模式,你需要定义一个带有getHelpDocuments()方法的HelpProvider接口,任何想提供帮助内容的bundle都需要实现这个接口并将其注册为服务。而如果采用扩展者模式的话会更加简便,因为能够跟踪到bundle的到来和消失。在现实世界中,扩展者模型用到了如下的地方:

  • Declarative Services是一个扩展者,它会查找其他bundle中的Service-Component声明,并且会执行初始化组件、注入服务等操作。
  • Blueprint也是一个扩展者,所做的事情很类似,它会查找Bundle-Blueprint声明或约定路径下的XML文件。
  • OSGi企业级规范所定义的JPA扩展者会查找Meta-Persistence头信息中所声明的persistence.xml。如果找到的话,将会为该bundle创建持久化单元。
  • Eclipse也包含了扩展者(所起的名字有点令人感到迷惑,叫做Extension Registry)。Eclipse使用它来创建视图、透视图以及菜单等,它们通过位于bundle根目录中的plugin.xml文件进行声明。

总而言之,服务用来基于契约注册和查找对象,而扩展者用来扩展bundle的功能,通常会基于某种类型的资源声明或头信息,而不是可执行的代码。

Christian Schneider是Talend的开源软件开发人员,主要工作内容包括Apache Camel、Apache Karaf以及Apache CXF,针对这个问题,他的看法是扩展者主要用于框架之中,它需要了解bundle的内部实现,因此不利于松耦合,但是其优势在于能够定义全新的抽象功能,如Blueprint、Declarative Services以及CDI,这些框架都是使用扩展者模型将bundle按照一定的规范装配在一起的。而服务对于应用本身来说是正确的选择,它能够隐藏实现的细节,同时能够避免服务的使用者实例化类,因此有利于实现松耦合。在实际中,我们经常会组合使用它们,如可以使用CDI注解来装配服务,在内部CDI会使用扩展者模型,而对于应用本身来说则使用服务来将bundle装配在一起。

在Peter Kriens的博文中,他将bundle、服务以及扩展者类比为日常的社会交流。在社会交流场景中,我们会扮演不同的角色,如商业交易中的销售方和购买方。在OSGi中,µservice模型就是这样的参与者。µservice比其他的服务模型更适合这样的场景,因为它是基于动态化的。

在社会化的类比中,bundle就是具体的人,它会控制行为并选择要参与什么样的场景,以及要使用或提供什么样的服务。但是有一些场景是重复性的、无聊的并且不是我们主要的兴趣所在,这时候使用服务就不太合适了。假设我有两辆汽车,其中一辆在探测到我有车钥匙时会自动打开车门,而另一辆做不到这一点,因此我会更喜欢前一辆。

我们可以使用OSGi模拟这样的场景,因为能够知道已经安装了什么bundle以及何时有新的bundle安装进来。就像那辆聪明的汽车能够探测到我口袋中的钥匙一样,bundle也可以查看其他bundle中的内容并作出相应的反应。Peter还讨论了数据库迁移的例子,同样使用了扩展者模式。数据库模式的扩展者bundle会根据其他bundle的模式描述文件,对数据库作出相应的变更,这样的话,一些凌乱的代码就能统一抽取到扩展者bundle中了,另一个类似的例子是在Declarative Services的实现中,会有一个专门的扩展者bundle来负责解析XML、处理依赖等繁琐的事情。Paremus的packager packs也使用这种模式来统一管理应用的生命周期。

那么究竟该何时使用服务模式,何时使用扩展者模式呢?

Peter认为,服务模型是关于bundle中核心领域内容的,它应该是bundle中主要的关注点所在,代码应该按照这种模式来进行编写。扩展者模式主要用来将杂乱和样板式的代码从领域bundle转移到一个特定的bundle之中。了解服务和bundle的动态性会极大地帮助我们理解这两种模型。

服务和扩展者都是在OSGi中十分重要和常见的模型,希望本篇文章中介绍的专家观点能够帮助您厘清它们的概念以及适用场景。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

扩展者模式和服务模式的使用场景 by Wong Peter

扩展者主要用于框架之中,它需要了解bundle的内部实现,因此不利于松耦合,但是其优势在于能够定义全新的抽象功能。而服务对于应用本身来说是正确的选择,它能够隐藏实现的细节,同时能够避免服务的使用者实例化类,因此有利于实现松耦合。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

1 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT