Declarative Services――Service-Oriented Component Model
Jeff 在 EclipseCon 2006 那篇介绍 Equinox 的 PPT 中提到的 Declarative Services( 文中全部采用 DS 简称 ) 的用法让人极度被吸引,但同时又产生怀疑,想起以前自己看过 DS 好像不是这样的,没这么强,便再次翻阅了 OSGI R4 中的 DS 的章节,以验证 Jeff 的说法, ^_^ ,仔细看过 DS 章节后,确实为 Declarative Services 的强大而感到高兴, DS 是一个面向服务的组件模型,从组件模型层次上去看,它超越了传统的组件模型,在组件模型描述的完备性上有了很大的进步,例如在组件服务的依赖上、组件服务的延迟加载上、组件服务的多样性控制上、组件服务的配置上以及组件服务的生命周期管理上,不过 DS 只能在 OSGI 容器中使用,这尽管看上去可能是个弱点,但作为 OSGI 规范中的一部分,这无可厚非,其思想值得很多目前 Component Model 的开源框架值得思考和学习,如感兴趣,请阅读 OSGI R4 中 DS 章节。
简介
DS 制定的目的是为了提供发布 / 查找 / 绑定服务的模型,作为 OSGI 中的规范,它的关注和 OSGI 其他的规范一样,都在于对于内存的占用、代码的大小、启动的快速以及使用的简易方面, ^_^ ,和一般做 java 的开源框架的那些可能不同,这也给我们带来了不同的视角,可以看到 DS 是怎么去考虑这些方面的,在内存的占用上, DS 采用提供组件的延迟装载以及强大的组件生命周期管理的方式来控制对于内存的占用以及启动的快速,使用的简易方面 DS 采用一个简单的 xml 描述来实现,加上它对于 Component 并没有太多的要求,所以用起来还是比较简单的。
Component 及其 Lifecycle
DS 作为 Service-Oriented Component Model ,双重的体现出了 Component 、 Service 的概念 ( 在 DS 中其实它的准确命名是 Service Component) ,一定程度上解决了以前经常可见的所谓的 Component-Oriented 以及 Service-Oriented 的混淆概念,在 DS 中采用的为定义 Component 的方式,每个 Component 可以暴露出多个服务,同时也可依赖于多个服务, DS 中有三种类型的 Component ,分别为 Immediate 、 Delayed 以及 Factory ,从字面上就可以理解了,在介绍这三种类型的 Component 之前先来讲讲如何在 DS 中定义 Component 以及 Component Satisfied 的含义。
在 DS 中定义 Component 采用的为通过 xml 描述的方式,在 DS 章节中有关于 Component xml 描述的详细介绍,具体可以参见该章节,此 xml 描述非常的简单, ^_^
Component Satisfied 的含义非常关键,在 DS 中 Component 的生命周期和这个有着密切的关系,象 Component 的激活就要求 Component Satisfied ,而当 Component 在运行过程中一旦出现 Unsatisfied 的现象 Component 就会被自动的注销,那么在 DS 中 Component Satisfied 的概念到底是指什么呢? Component Satisfied 可以这么理解,它是 Component 激活的前置条件,它主要由两点来决定是否为 Satisfied :
1、 Component 是 Enabled ;
2、 Component 的配置可被引用和解析、 Component 中引用的 Service 同样也是 Satisfied 的,同时引用的 Service 至少有一个是处于可用状态的,或者引用的 Service 中配置了可为 0 个可用状态 service 。
再回到三种类型的 Component ,分别看看他们的生命周期是怎么样的:
三种类型的 Component 的启动都依赖于 Component 处于 Satisfied 的状态下:
Bundle 启动时 DS 装载相应的配置文件 ( 通过在 mainfest.mf 中指定 Service-Component: 配置文件 ) ,解析配置文件,合并其中的组件的配置部分,获取引用的 Service ,如上面的几个步骤全部通过,那么 DS 就认为这个组件是 Satisfied 的。
1、 Immediate
对于 Immediate Component , DS 会立刻激活这个 Component 。
在这个 Component 的配置文件被改动、配置信息文件 (properties 文件 ) 被改动以及所引用的 Service 被改动的情况下, DS 都会重新装载并激活这个 Component ,同时对于引用了这个 Component 中 Service 的 Component 也会做同样的动作。
2、 Delayed
对于 Delayed Component , DS 会根据配置文件中的 Service 的配置,注册 Service 的信息,但此时不会激活这个 Component 。
直到这个 Component 被请求的时候 DS 才会去激活这个 Component ,相应的设置引用的 Service 。
在这个 Component 引用的 Service 发生改变的情况下, DS 不会重新装载这个 Component ,而会采用调用配置中设置的 bind 、 unbind 方法来重新设置引用的 Service 。
3、 Factory
Factory Component 和 Immediate Component 基本相同,不过它在激活后注册的只是一个 ComponentFactory 服务,只有在调用 ComponentFactory 的 newInstance 后才会激活里面的各个组件。
这三种类型的组件中无疑 Delayed Component 是最需要的,为系统的动态性带来了很大的好处,同时也节省了内存的占用。
组件的生命周期受 bundle 生命周期影响,当 bundle 停止时 bundle 中所有组件也就停止。
Service 的发布 / 查找 / 绑定
既然是 Service-Oriented ,最为关心的当然是 Component 中 Service 的发布 / 查找 / 绑定,分别来看看:
u Service 的发布
对于 Component 中 Service 的发布,在 DS 中非常简单就可以做到了,只需要在 Component 的 xml 中编写如下一段:
<service>
<provide interface=”net.bloajva.DemoService”/>
</service>
这样外部的 DS 就可以引用这个 interface 的 service 了,可以看到,在这个 xml 的属性里采用的是 interface 的声明方式,尽管其实这个 interface 里也是允许填入实际实现服务接口的类名的,但不鼓励这么做。
可以看到 Service 的发布非常的简单,只要 Component 实现了相应的 Service 的接口即可。
u Service 的查找
DS 中也提供类似 jndi lookup 那样的方式,在 Component 中可以通过定义 activate(ComponentContext context) 这样的方法来获得 ComponentContext ,通过 ComponentContext 就可以获取到在 Component 配置文件里定义的引用的 Service 了。
配置文件类似这样:
<reference name=”LOG” interface=”org.osgi.service.log.LogService”/>
代码类似这样:
public void activate(ComponentContext context){
LogService log=(LogService)context.locateService(“LOG”);
}
^_^ ,很简单的一种方式。
u Service 的绑定
DS 中提供直接绑定 service 的方法,按照 DI 的观点来说其实就是注入 service 的支持, DS 提供的是类似 setter 注入的方式,只是更加的强, ^_^ ,因为 DS 会根据引用的 Service 的状态来相应的调用这个 setter 注入,在 DS 中要采用这种方式也很简单。
配置文件类似这样:
<reference name=”LOG” interface=”org.osgi.service.log.LogService” bind=”setLog” unbind=”unsetLog”/>
代码类似这样:
public void setLog(LogService log){
this.log=log;
}
public void unsetLog(){
this.log=null;
}
可以看到这和目前流行的 setter DI 方式完全相同,而且更强, ^_^ , DS 帮忙监听了所引用的 Service 的生命周期状态,会根据 Service 的生命周期状态相应的调用 unbind 方法,连监听器都省了, ^_^ ,后面再介绍下 Service bind 的 static reference 和 dynamic reference 的方式,那时就会发现它更强了,呵呵
这样看下来,可以看出 DS 要实现现在的 DI 方式同样完全是可以的,而且有更为强大的支持,在 DS 的情况下也是可以采用纯 POJO 方式的 Component 的 ( 因为象上面提到的 activate 那个方法是可以不写的 ) ,爽,对于以前用过 OSGI 中通过 ServiceRegistry 以及 ServiceReference 来发布 / 查找服务的同学们来说就会更有体会了, ^_^
更多
DS 提供的不仅仅是上面所说的那些,还有很多非常强的功能,例如:
u Component 配置
在 Spring 的 bean 的配置文件中可以引用外部的配置文件的配置信息,在 DS 中同样可以,而且更为简单,在 DS 中只需要在 Component 的 xml 描述文件中采用类如 property 来直接指定属性或采用 properties 来指定引用外部的配置文件即可,而且这个配置文件的属性在 Component 中可以通过 ComponentContext 来获取。
u Service 的 Static 、 Dynamic 引用
对于 Component 中引用的 Service , DS 可以采用两种策略,一种是 static ,另外一种是 dynamic ,默认情况下是 static 的方式,在采用 static 方式的情况下,如引用的 service 发生了变化,那么整个 Component 都会重新装载并激活,而在 dynamic 方式的情况下,只会重新调用其中的 bind 、 unbind 方法。
在 DS 中通过在 reference 元素中增加 policy 的属性来控制采用 static 或 dynamic 方式。
u 引用的 Service 的过滤
这个的含义其实在目前其他的 Component Model 中可能较少碰到,在 DS 中是支持一个 Service Interface 由多个 Component 实现并暴露多个实现的 service 的,在这种情况下可能希望只引用某种 service 的实现,那么在 DS 中可以通过在 reference 元素中增加 target 属性来声明对于引用的 service 的过滤。
u Cardinality
这个词不知道该怎么翻译好,就没翻译了,它里面指的主要是两点,一个是多样性,一个是引用的 service 的数量的控制,这点挺强的, ^_^..
可以通过指定 optionally 属性值来决定引用的 service 的数目为 0 到多、 1 个、 1 到多或其他情况。
要将 Component 进行部署非常简单,在编写了上面的 Component 的 xml 描述文件后,只需要在现在的 Bundle 中增加 Service-Component 这个属性即可完成部署工作。
作为 OSGI 规范大家庭中的一个小部分,其可以充分利用 OSGI 规范中其他的 Service 来增强相应的功能,如 Security Service 来增强安全性等。
对于传统的 Component Model 来说, DS 在 Component 的 Service 的发布 / 查找 / 绑定方面的机制、 Component 的生命周期管理机制、引用的 Service 的管理机制上都值得学习, ^_^
目前 DS implemention 还没有正式发布,按照 roadmap 来看的话要等到 eclipse 3.2 正式版发布后才能 release ,还得等等, ^_^ ,先去 CVS 上下目前的 implemention 来用用,看看目前的 DS implemention 有多强了 …