OSGI中的service依赖关系管理
??? 众所周知,对于高动态高可扩展的应用,OSGI是一个非常好的平台。但是,也因此增加了复杂性,开发中对service的依赖变得复杂。这也是service的关系管理成为OSGI中一个非常重要的部分,我们来看看OSGI中service依赖关系管理的方式。篇幅原因,只关注发展历程,不具体介绍每个方式的详细实现细节。
??? 概括的说,目前在OSGI中主要有以下几种service依赖关系管理的方法:
??? 1. Service listener
??? 2. Service binder
??? 3. Dependency Manager
??? 4. Declarative Services
??? 5. iPOJO
??? 6. blueprint
???
1) Service Listener
??? 这是OSGI中原生的service依赖管理机制,是最简单直接的方式,其基本原理非常简单,标准的注册/查找:
???
??? 1. 被依赖的bundle通过BundleContext.registerService()方法注册服务到系统中
??? 2. 使用依赖的bundle在start时通过BundleContext的getServiceReferences()/getService()来查找依赖的service
??? 3. 使用依赖bundle通过BundleContext.addServiceListener()来分别注册ServiceListener
??? 4. 在被依赖的bundle/service状态发生变化时, 使用依赖bundle通过ServiceListener的serviceChanged()得到通知并作出调整。
??? 在这种方法中,使用依赖的Service必须进行大量的编码工作来完成对依赖的service的关系管理,需要处理琐碎细节如各个Service的运行时状态变化。为了减少工作量,OSGI设计了ServiceTracker来简化对依赖service的编码工作,即ServiceTracker将负责处理上述步骤中的2/3/4。
??? 经过ServiceTracker优化后的Service Listener机制,还是存在一些缺点:
???
??? 1. 编码量还是不小,尤其对于依赖较多的场景
??? 2. Activator 还是太复杂了,尽管已经很努力的试图简化
??? ??? 对于一些业务逻辑简单的service,如果依赖的service比较多,那么Activator的逻辑和代码实现远比service本身的逻辑和实现要复杂,这违背了我们使用框架简化开发的初衷。
??? 3. Activator对测试不利
??? ??? 这个是Activator的复杂性造成的,由于Activator中存在大量的依赖处理逻辑,理所当然的会增加测试的复杂性。
??? 总结说,Service Listener 机制下,管理service依赖对于开发者来说完全是个重体力活: 很重要,经常要做,容易出现错误, 出错时不容易测试。而且,这些工作都不是service 业务逻辑的组成部分,不能带来直接收益。简言之,吃力不讨好,一不小心就搬石头砸自己的脚。
??? 更重要的,从分工的角度上将,开发人员应该将更多的精力投入与应用的逻辑,而不是OSGI的底层实现机制。因此,从2000之后,就陆续有人开始考虑对此改进。
2) Service binder
??? Service binder OSGI针对这个问题的一个尝试,基本出发点非常明确:希望找到一个通用的方法来简化OSGI下的动态service依赖管理.
??? 最初开始这个工作的是两位大牛,Richard S. Hall和Humberto,大概在2002年的时候开发完成。
??? 看我们看Service binder是怎么做的,基本步骤为:
??? 1. 提供一个org.apache.felix.servicebinder.GenericActivator;
??? ??? 现在bundle的Activator只要简单的继承GenericActivator就可以了,所有的代码实现在GenericActivator提供,减少代码的目标顺利达成。这个可以说足够简单到没有办法再简单了。
??? 2. 通过metadata.xml 来实现service的依赖注入
??? 3. 具体的service的实现类基本就是一个干净的POJO了
??? ??? 当然还是需要实现bind-method/unbind-method 两个方法,好在这个是通过metadata.xml来做映射,不是另外提供接口定义,因此勉强避开了"侵入"的问题。
??? Service binder 机制在减少Activator方面成效显著,但是引入的metadata.xml文件似乎又带来了新的问题,xml配置文件的可维护性个人感觉值得怀疑。用过EJB的同学都明白,EJB的部署文件有多令人烦恼。
??? 当然Service binder 的思路非常的正确:通过引入了自动化的service依赖关系管理,简化开发,允许开发人员可以集中精力在service的实现上,而不是疲于处理依赖关系管理。
??? Service binder的实现似乎并没有被推广开,因为很快OSGI就在2004年的OSGI R4规范中引入了Declarative Services。因此Felix也就终止了对Service binder的后续支持。
??? ???
3) Dependency Manager
??? 继Service binder之后,Felix又提供了名为Dependency Manager 的service依赖管理方式,对比Service binder,个人感觉这个Dependency Manager 只是针对Service binder的一个改进:将metadata.xml 文件取消,由新引入的DependencyManager来实现metadata.xml 文件的功能。原来在metadata.xml 文件中的配置转变为在Activator中通过代码调用DependencyManager来实现.
??? Dependency Manager其实现的方式为:
??? 1. 提供org.apache.felix.dependencymanager.DependencyActivatorBase
??? ??? bundle的Activator需要继承DependencyActivatorBase,并实现DependencyActivatorBase要求的init()/destroy()方法
??? 2. 在init()中,可以通过DependencyManager 对象来注册服务,并注明依赖。
??? 3. 具体的Service类可以是POJO,DependencyManager 通过反射来注入依赖的service。
??? Felix 官方给出了一个Dependency Manager的使用示例
http://felix.apache.org/site/dependency-manager-usage.html
??? 从示例上看,对service的依赖管理已经简化了许多。
??? 这里还有一个05年的介绍Dependency Manager的 presentation:
http://felix.apache.org/site/presentations.data/DependencyManagement.pdf
4) Declarative Services
??? 2004年发布的OSGi的4.0版本中,加入了Declarative Services,据说是从Service Binder进化而来。
??? Declarative Services的实现方式和Service Binder的确非常相似:
??? 1. 同样是需要一个xml文件来配置
??? ??? 在 bundle manifest中增加Service-Component 的header
??? ??? 提供的功能和Service Binder很类似,配置方法也很接近。
??? 2. 同样的提供bind/unbind 方法的配置
??? ??? 对于每个依赖的service,都可以在配置文件中通过指定bind/unbind 方法来处理依赖的状态变化。
??? 此外,Declarative Services 提供两个特殊的lifecircle方法:
??? ??? protected void activate(ComponentContext context)
??? ??? ??? protected void deactivate(ComponentContext context)
??? 如果service实现类提供了这两个方法,则Declarative Services 会自动识别并调用这两个方法。注意这两个方法没有接口定义进行强约束,只是一个约定而已,估计是为了避免OSGI对service的侵入。
??? Declarative Services 是OSGI规范的一部分,因此Declarative Services的支持自然是各个OSGI实现都提供的。
??? 从功能上将,Declarative Services 基本已经不错了,但是大牛们的脚步并未就此停住。
5) iPOJO
??? 2005年,Richard 开始考虑使用字节码生成技术来进行创建组合服务的改进,另外一个牛人Peter Kriens也同样的工作并实现了一个原型。
??? 2006年,Clement Escoffier 和 Richard 开始开发iPOJO,合并了Peter Kriens之前的工作内容,这就是iPOJO的由来。
??? 对于iPOJO的定义,Felix的iPOJO页面有如下说明:iPOJO是一个服务器组件运行时,目标在于简化OSGI应用开发。原生支持所有的OSGI活力。给予POJO的概念,应用逻辑开发简单。非功能性的属性在运行时被注入到组件中。
??? 同样看看Felix对iPOJO优点的说明:
???
??? 1. 组件被作为POJO开发,不需要其他任何东西
??? 2. 组件模块是可扩展的,因此可以自由的适应需要
??? 3. 标准组件模型管理service 供应和service 依赖,所以可以要求其他任何OSGI服务来创建组合服务,
??? 4. iPOJO管理组件实例的生命周期和环境动态
??? 5. iPOJO提供一个强力的组合系统来创建高度动态的应用
??? 6. iPOJO支持注解,xml或者基于Java的API来定义组件
??? 可以看到iPOJO的功能远比之前的几个解决方案要强大,除了支持Declarative Services已经实现的功能外,还提供了强大的注解支持,而且实现了组合系统,这些对于开发大型的复杂应用时非常有用的。
??? Richard 在他的presentation谈到iPOJO 的设计思路:
??? 1. Make things simple / 让事情简单
??? 2. Follow POJO philosophy / 遵循POJO的哲学
??? 3. Employ byte code manipulation techniques / 使用字节码操纵技术
??? 4. Be as lazy as possible / 尽可能的偷懒
??? 目前的iPOJO还在继续发展中,最新的一个版本iPOJO 1.6.0在2010-04-25发布。
?
?
6) blueprint
?
blueprint 是OSGI为了解决上述问题的最新尝试,在去年刚发布的OSGI v4.2 规范中新加入了 Blueprint Container 的规范内容。
?
提到blueprint 就不能不提到spring Dynamic Modules,blueprint 可以认为是Spring Dynamic Modules版本的改进和标准化。SpringDM 1.x版本实现了Spring Dynamic Modules for OSGi,在Spring Dynamic Modules被标准化为Blueprint Container 规范后,新的SpringDM 2.x 则成为Blueprint的参考实现。
?
blueprint 的使用实行非常类似标准的spring IOC容器,比如同样的使用xml配置文件,只是从ioc 的applictionContext?xml变成了Blueprint XML。而Blueprint XML的配置方式和spring 有惊人的相似。
举例,最简单的bean:
?
?? <bean id="accountOne" value="#1 account"/>
?? </bean>
?
基本就是照搬spring IOC的方式,对于熟悉spring的开发人员来说无疑是个好消息,起码学习曲线平缓了。
?
由于是OSGI的标准规范,blueprint 目前的支持还是不错的,除了上面说的SpringDM外,还有Geronimo Blueprint?Container 和 Apache Felix Karaf 都提供了对blueprint的支持。
???
??? 总结,从上述的发展历程上看,OSGI中的service依赖关系管理方式,经历了从简单原始到逐渐成熟强大的过程,前后经历了大概10年的时间.