OSGI中Declarative Services的运用
前言
Declarative Services,即所谓的声明式服务,我在前文中曾经提及到注册式服务与声明式服务,但是在前文中并没有提及怎么使用声明式服务,只是简单的说了下概念和相对于blueprint来说有哪些优缺点,总而言之,可谓是一笔带过,这几日想起这个,还是决定需要仔细的讲一下声明式服务。
简介
Declarative Services,这是在OSGi 4以后的规范中出现的,在这里引用一段其他人说的话,Declarative Services 是一个面向服务的组件模型,它制订的目的是更方便地在 OSGi 服务平台上发布、查找、绑定服务,对服务进行动态管理,如监控服务状态以及解决服务之间的复杂的依赖关系等问题。Declarative Services 采用服务组件的延迟加载以及组件生命周期管理的方式来控制对于内存的占用以及启动的快速,很好的解决了传统的 OSGi 服务模型在开发和部署比较复杂应用时内存占用大、启动慢等问题,并且对服务组件的描述采用XML来实现,十分便于用户理解和使用。
以上几句话几乎可以在任何一段介绍osgi compendium都会提到,在我这也不免俗要提到几句,但是在此我更多的还是要介绍怎么使用而并不简单的讲解osgi compendium里面最新有哪些功能点,有哪些新的规范需要了解,我觉得这些只需要在官网中自己多去看几遍就可以了解,以下就是网址,IBM上的文章:
OSGi 中的 Declarative Services 规范简介
原版osgi compendium的规范为:
osgi compendium原文文档r4.cmpn.pdf
以上,对这些规范都讲得十分清楚,我在此不再赘述,仅仅是讲解在我使用Declarative Services发布一个服务之后,如何在另外一个Bundle中怎么使用这个服务,在此提一句,不要在编写Declarative Services的Bundle中使用该服务,会出现一些问题,这个在稍后会讲出来。
编写声明式服务
在使用Declarative Services的时候,要确保我们在pom文件中加了对compendium的支持,相关maven依赖
如下:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<version>${org.osgi.version}</version>
<scope>provided</scope>
</dependency>
我在这里编写了一个简单的程序,一个接口Test,提供一个hello方法,然后是一个实现类TestComponent,实现这个方法,具体以上的源码为:
Test:
package cn.com.ds;
/**
* Created by xiaxuan on 16/7/13.
*/
public interface Test {
String hello(String name);
}
TestComponet:
package cn.com.ds;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.log.LogService;
/**
* Created by xiaxuan on 16/7/13.
*/
public class TestComponent implements Test {
public String hello(String name) {
return "hello " + name;
}
public void activate(ComponentContext context) {
System.out.println("activate(" + context + ")");
}
public void deactivate(ComponentContext context) {
System.out.println("deactivate(" + context + ")");
}
public void modified(ComponentContext context) {
System.out.println("modified(" + context + ")");
}
public void bind(LogService service) {
service.log(LogService.LOG_INFO, "bind");
}
public void unbind(LogService service) {
service.log(LogService.LOG_INFO, "unbind");
}
}
这个里面active和deactive方法是用来监控在我们的服务在激活和钝化的时候使用的,在此处打出日志,说明组件被激活了,源码程序比较简单,现在来观察一下配置文件的编写,这个需要在resources目录下创建一个OSGI-INF的文件夹,然后创建文件components.xml,在xml中进行相关的配置,如下:
<?xml version = "1.0" encoding = "UTF-8"?>
<component name="TestComponent">
<implementation class="cn.com.ds.TestComponent" />
<service>
<provide interface="cn.com.ds.Test"/>
</service>
<reference bind="bind" cardinality="1..1"
interface="org.osgi.service.log.LogService" name="LogService"
policy="dynamic" unbind="unbind" />
</component>
我在component中编写一个service,用以向外提供服务,同时还引用了LogService的服务,我们在实现的方法中,反复使用了LogService这个服务,在这里需要提一下的就是,我在这使用的是配置文件的形式来使用声明式服务,但是osgi还可以提供通过注解的方式来配置我们的组件,同样可以达到相同的效果,在上述完成之后,我们在当前Bundle之中要向外暴露这个服务,需要我们在felix的插件中加一行为如下:
<Service-Component>OSGI-INF/components.xml</Service-Component>
如此,在install完成后的Bundle中的mf文件之中,就会暴露这个服务。
使用声明式服务
使用这个服务相当简单,在feature配置文件中引入ds这个Bundle,然后在pom依赖中也加上ds的依赖,就可以使用ds这个Bundle提供的服务了,使用相当简单,我在Activator中编写的使用方法,和之前的ServiceTracker写在了一起,就是简单的使用context获取服务,然后使用,如下:
package cn.com.example;
import cn.com.ds.Test;
import cn.com.ds.TestComponent;
import org.osgi.framework.*;
/**
* Created by Administrator on 2016/6/18.
*/
public class Activator implements BundleActivator {
HelloServiceTracker serviceTracker;
ServiceRegistration serviceRegistration;
public void start(BundleContext bundleContext) throws Exception {
System.out.println("service registered...");
//开启服务跟踪器
serviceTracker = new HelloServiceTracker(bundleContext);
serviceTracker.open();
// //注册服务
// serviceRegistration = bundleContext.registerService(HelloService.class, new HelloServiceImpl(), null);
// //获取b被跟踪的服务
// HelloService helloService = (HelloService) serviceTracker.getService();
// if (helloService != null) {
// System.out.println(helloService.hello("xiaxuan"));
// }
ServiceReference serviceReference = bundleContext.getServiceReference(Test.class);
Test testComponent = (Test) bundleContext.getService(serviceReference);
System.out.println(testComponent.hello("bingwen"));
}
public void stop(BundleContext bundleContext) throws Exception {
System.out.println("Stopping service");
//关闭服务跟踪器
if (serviceTracker != null) {
serviceTracker.close();
}
//注销服务
serviceRegistration.unregister();
}
}
以上这个Activator和之前在ServiceTracker中编写的Activator没有任何区别,我把两个知识点的写到了一起,其中使用ds服务的代码就是在start中的最后两行。
ServiceReference serviceReference = bundleContext.getServiceReference(Test.class);
Test testComponent = (Test) bundleContext.getService(serviceReference);
System.out.println(testComponent.hello("bingwen"));
在使用上,没有任何区别。
程序运行
现在启动karaf,观察结果:
在这里可以看到我们正常使用Test服务,Test获取到了相应实例。在这里正常使用了ds。
总结
- 相比于Declarative Services来说,个人还是更喜欢使用blueprint一些,毕竟相对于ds,blueprint更加方便。
- 在ds声明中的Bundle中,不要在Activator中使用该服务,因为component的active的时间在Activator中的start方法之后,并且component的销毁时间也在stop方法之前,怎样都获取不到相应的实例,因此个人认为在一个Bundle中声明服务,在另外一个Bundle中引用最好不过。
- 下一篇文章将讲解Bundle之间的EVentadmin通信过程。