osgi之ServiceListener的应用
前言
我原想直接跳过这些osgi中基础知识,直接从osgi应用的一些中级篇或者高级篇开始的,后来想到osgi中的ServiceListener、ServiceTracker、Ds还是都需要讲一下,所以决定将osgicommand后续中的内容放一下,从ServiceListener开始讲一下,不过这里就不再讲述felix官网中的ServiceListener的example,而是自己单独编写example和进行相关讲解。
ServiceListener
/**
* A {@code ServiceEvent} listener. {@code ServiceListener} is a
* listener interface that may be implemented by a bundle developer. When a
* {@code ServiceEvent} is fired, it is synchronously delivered to a
* {@code ServiceListener}. The Framework may deliver
* {@code ServiceEvent} objects to a {@code ServiceListener} out
* of order and may concurrently call and/or reenter a
* {@code ServiceListener}.
*
*/
ServiceListener解释如上所示,本义就是这只是一个接口,在进行相应的实现之后,可以用来监控某些服务,在服务注册、修改、注销的时候,进行相应的其他调用,现在我将讲述这个ServiceListener的具体使用,在使用过程中,再来讲解中间会有那些问题。
ServiceListener用法1
先分别提供一个接口和一个实现类,分别为HelloService和HelloServiceImpl,代码如下:
HelloService:
package cn.com.example;
/**
* Created by xiaxuan on 16/7/12.
*/
public interface HelloService {
String hello(String name);
}
HelloServiceImpl:
package cn.com.example;
/**
* Created by xiaxuan on 16/7/12.
*/
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
return "hello " + name;
}
}
都很简单,就是提供一个简单的服务,其中有一个方法为hello,真正的重点还是在Activator中,现在先贴出Activator中的代码,随后再来讲解其中监听服务和容易出现的问题:
package cn.com.example;
import org.osgi.framework.*;
/**
* Created by xiaxuan on 16/7/12.
*/
public class Activator2 implements BundleActivator, ServiceListener {
ServiceRegistration serviceRegistration;
BundleContext context;
public void start(BundleContext context) throws Exception {
this.context = context;
context.addServiceListener(this);
System.out.println("service registered....");
serviceRegistration = context.registerService(HelloService.class, new HelloServiceImpl(), null);
}
public void stop(BundleContext context) throws Exception {
context.removeServiceListener(this);
System.out.println("service unregistered...");
serviceRegistration.unregister();
}
public void serviceChanged(ServiceEvent event) {
switch (event.getType()) {
case ServiceEvent.REGISTERED:
//获取service引用
ServiceReference ref = context.getServiceReference(HelloService.class);
//获取service实例
HelloService helloService = (HelloService) context.getService(ref);
if (helloService != null) {
//调用service方法
System.out.println(helloService.hello("xiaxuan"));
//释放service
context.ungetService(ref);
}
break;
case ServiceEvent.UNREGISTERING:
System.out.println("serviceChanged find service unregistered.");
break;
}
}
}
我们在Activator中实现ServiceListener之后,还需要将自己本身加入到监听器中,在serviceChanged方法中,当服务出现变化的时候可以在这个方法中监听到,其中或是注册,注销,或是修改都可以做出监听,现在我们打上断点,监听相关服务。
服务启动
服务启动过程就不再截图了,这次我们在stop bundle,然后再start bundle的时候再来调试和查看相应的信息,现在展示karaf已经成功启动的截图,如下所示:
可以看到example这个Bundle已经正常启动,现在先使用stop命令,实质上在karaf中这个命令是Bundle:list命令,在karaf中大量的运用命令,在之后若有时间将会讲一下karaf的源码,讲一下这些命令,这个时候example的id为8,我们使用stop 8停止这个Bundle,执行命令之后如下所示:
当Bundle中的state为resolved的时候,这个Bundle已经停止下来了,然后我们再启动Bundle,命令为start 8,这个时候我们观察我们的断点,这个断点设置在serviceChanged方法中,如下所示:
我们放开断点,这个时候程序再次进入断点,如下:
发现这次进入断点的时候,event类型和上次有所不同,说明这里的ServiceListener的serviceChange方法对当前bundle启动的手,所有的服务注册都做了监听,这个时候,我们去掉断点,观察控制台,会发现打了许多次的’hello xiaxuan’,如下:
打了五次的hello xiaxuan,说明有五次的服务注册,每一次服务注册都进来获取了一次HelloService,然后调用了一次服务,在控制台打印一次,ServiceListener并没有对具体某个服务进行监听。
ServiceListener用法2
在以上ServiceListener的用法中,并不仅是在Activator中实现ServiceListener来做到监听服务,可以在start方法中直接实现,如果Activator类实现ServiceListener接口,还需要将本身加进Listener中,代码如下:
context.addServiceListener(this);
如果在start方法中实现的话,代码如下所示:
package cn.com.example;
import org.osgi.framework.*;
/**
* Created by xiaxuan on 16/7/13.
*/
public class Activator3 implements BundleActivator {
ServiceRegistration serviceRegistration;
BundleContext context;
public void start(BundleContext bundlecontext) throws Exception {
this.context = bundlecontext;
System.out.println("service registered....");
serviceRegistration = context.registerService(HelloService.class, new HelloServiceImpl(), null);
context.addServiceListener(new ServiceListener() {
public void serviceChanged(ServiceEvent event) {
switch (event.getType()) {
case ServiceEvent.REGISTERED:
//获取service引用
ServiceReference ref = context.getServiceReference(HelloService.class);
//获取service实例
HelloService helloService = (HelloService) context.getService(ref);
if (helloService != null) {
//调用service方法
System.out.println(helloService.hello("xiaxuan"));
//释放service
context.ungetService(ref);
}
break;
case ServiceEvent.UNREGISTERING:
System.out.println("serviceChanged find service unregistered.");
break;
}
}
});
}
public void stop(BundleContext context) throws Exception {
System.out.println("service unregistered...");
serviceRegistration.unregister();
}
}
直接在addServiceListener中new ServiceListener,然后实现相关方法,相比较而言还简单一点,不过实现的效果相同。
总结
- 使用ServiceListener时,只在Service发生相关变更的时候获取开销,可以动态感知service的注册和注销。
- 但是问题在于在ServiceListener注册之前已经存在的Service无法监听到,需要自己维护service的获取和释放,如果需要监听多个service的话,十分不方便。
- 只要是服务发生变更的时候就进行相关监听,其实在许多时候无需监听其他服务,有时候用起来比较麻烦。
- 下一节中将讲解ServiceTracker,这是对ServiceListener的进一步封装,能够有效的监听我们所需的服务。