上接 基于Equinox开发OSGi应用(一)
开发一组计算器bundle实例
本节讲到的例子是仿照网上甚为流行的一个例子,但苦于一直未找到源码,网上贴的都是一些转帖,代码片段,估计初学者很难将其还原并调通!我最开始弄这个咚咚的时候,其过程之痛苦,难以言喻,所以想着仿照该例子的设计,给予实现,文后贴出源码,希望能帮到大家。
该例子是一个关于计算器的实例,osgi.example.compute bundle(下文简称compute bundle)提供了统一的计算接口:Compute,另外两个bundle分别为osgi.example.compute.add(下文简称add bundle)和osgi.example.compute.multiply(下文简称multiply bundle),在这两个bundle中,各自对compute bundle进行不同的实现,一个实现加法,一个实现乘法。另外还有一个服务消费者osgi.example.compute.consumer bundle(下文简称consumer bundle),consumer bundle负责消费add bundle和multiply bundle提供的服务。上述4个bundle之间的关系如下图所示:
创建4个bundle之后的工程目录如下图所示:
通过该示例,将讲解如下内容:
l bundle之间如何实现包依赖
l bundle如何将服务注册到Equinox运行环境
l bundle如何调用其他bundle已注册的服务
1. 导出包
首先,我们来看看compute bundle,它只对外公布Compute接口,除此之外,bundle不包含其他内容,因此在它的MANIFEST.MF文件中,只需声明Export-Package即可,如下所示:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Compute Plug-in
Bundle-SymbolicName: osgi.example.compute
Bundle-Version: 1.0.0
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.osgi.framework;version="1.3.0"
Export-Package: osgi.example.compute
2. 引入包
add bundle和multiply bundle需要实现compute bundle的公布的Compute接口,同时又要将自身的Add、Multiply类公布给其他bundle,所以需要声明Export和Import两项内容,如下所示——multiply bundle的MANIFEST.MF和add bundle相似:
提示:MANIFEST.MF文件中有个细节需要注意,所有的头部,如Import-Package,都必须顶头写,如果该头部信息较长,需要换行,则换行需要空至少一格,否则会报错。
这样,我们将osgi.example.compute包引入add bundle中,可以像本地接口一样使用Compute接口了,如果足够细心的话,你将发现add bundle的工程目录结构发生了一个小小的变化,如下图所示:
osgi.example.compute工程被添加至当前工程的插件依赖环境中。
public void start(BundleContext context) throws Exception {
context.registerService(Add.class.getName(), new Add(), null);
}
Equinox提供了两种服务注册方式:
l registerService(String,Object,Dictionary) 注册服务对象 object 到服务名 String 下,可以携带一个属性字典 Dictionary;
l registerService(String[],Object,Dictionary) 注册服务对象 object 到服务名数组 String[] 下,可以携带一个属性字典 Dictionary,即一个服务对象可以按照多个接口名字注册,因为类可以实现多个接口;
4. 消费服务
相应的,如何消费add bundle注册的服务呢?让我们看看consume bundle中的Activator类的start方法:
ServiceReference addReference = context.getServiceReference(Add.class.getName());
Compute compute = (Compute)context.getService(addReference);
System.out.println(compute.compute(5, 6));
ServiceReference multiplyReference = context.getServiceReference(Multiply.class.getName());
compute = (Compute)context.getService(multiplyReference);
System.out.println(compute.compute(5, 6));
首先我们看到的是getServiceReference方法,通过该方法可以查询已注册的服务,Equinox提供两种查询方式:
l getServiceReference(String):根据服务的名字得到服务的引用;
l getServiceReferences(String,String):根据服务名和另外一个过滤器名字对应的过滤器得到服务的引用;
之后,根据查询到的服务,从bundle context中获取相应的服务,即可完成服务调用。
5. 运行bundle
根据helloworld讲到的运行方式,将compute、add、multiply、consumer4个bundle装载到Equinox中,运行结果如下:
osgi> 11
30
键入命令ss ,可以看到各个bundle的状态:
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.3.R34x_v20081215-1030
1 ACTIVE osgi.example.compute.multiply_1.0.0
2 ACTIVE osgi.example.compute.consumer_1.0.0
3 ACTIVE osgi.example.compute_1.0.0
4 ACTIVE osgi.example.compute.add_1.0.0
运行成功!
总结:通过阅读本文,初步掌握了如何利用Equinox开发OSGi应用,了解了bundle之间是如何进行包依赖的,如何注册、查询及消费服务的。
利用OSGi框架,我们可以很好实现软件的模块化开发,这一点有别于传统的模块化的概念,传统的模块化开发方式,只是从代码组织上进行模块化划分,而并未做到ClassLoader的隔离,模块与模块之间仍然可以通过Classpath完全暴露,而OSGi的各个bundle都拥有自己的ClassLoader环境,如果bundle内部的包未被导出,那么对于外界是不可见的。加之OSGi支持热部署,并且具备版本识别能力,即相同ID的bundle可以同时部署不同的版本,这样并不会引起冲突,这将极大的推动动态模块化开发的进程。
读完本文之后,我相信大家应该能够体会到OSGi所带来的好处,可是有一个很大的问题,因为OSGi在客户应用领域仍处于起步阶段,一些常用的第三方开源Jar包原本并不符合OSGi的bundle规范,还需要进行大量的改造工作,这也就是上文所说的OSGi的生态环境还不健全,比如我们平常所用的struts、jdbc驱动、数据库连接池、log4j等等,不过,我相信在不久的将来,这些问题都会解决的,我想那个时候真正的OSGi时代也就真的到来了。