OSGi 系列(七)之服务的监听、跟踪、声明等
OSGi 系列(七)之服务的监听、跟踪、声明等
1. OSGi 服务的事件监听
和 bundle 的事件监听类似,服务的事件监听是在服务注册、注销,属性被修改的时候,OSGi 框架会发出各种不同的事件供事先注册好的事件监听器处理。
1.1 服务的事件监听简介
服务的事件类型:
事件名称 | 描述 | 事件值 |
---|---|---|
REGISTERED | 服务被成功注册 | 1 |
MODIFIED | 服务属性被修改 | 2 |
UNREGISTERING | 服务被卸载 | 4 |
MODIFIED_ENDMATCH | 服务属性被修改,且不再匹配当前监听 | 8 |
注册服务监听:
bundleContext.addServiceListener(listener)
bundleContext.addServiceListener(listener, filter)
移除服务监听:
bundleContext.removeServiceListener(listener)
1.2 实战演示
(1) 在 email-client 中修改 BundleActivator
@Override
public void start(BundleContext context) throws Exception {
//1. 获取163的服务
ServiceReference<?>[] refs = context.getServiceReferences(EmailService.class.getName(), "(vendor=163)");
if (refs != null) {
for (ServiceReference ref : refs) {
EmailService emailService = (EmailService) context.getService(ref);
System.out.println(emailService);
}
}
context.addServiceListener(new ServiceListener() {
@Override
public void serviceChanged(ServiceEvent event) {
System.out.println(event.getSource() + " ==> " + event.getType());
}
});
}
测试结果如下:
可以看到 email-client 中注册的服务正在监听 email-service-163 服务状态改变
(2) filter 的使用与服务的获取类似
修改 email-service-163 中服务的注册:
@Override
public void start(BundleContext context) throws Exception {
Dictionary properties = new Hashtable<>();
properties.put("vendor", "163");
serviceRegistration = context.registerService(EmailService.class.getName(), new EmailServiceFactory(), properties);
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
;
}
Dictionary properties2 = new Hashtable<>();
properties.put("vendor", "456");
serviceRegistration.setProperties(properties2);
}).start();
}
修改 email-client 中服务的监听:
context.addServiceListener(new ServiceListener() {
@Override
public void serviceChanged(ServiceEvent event) {
System.out.println(event.getSource() + " ==> " + event.getType());
}
}, "(vendor=163)");
测试结果如下:
2. OSGi 服务跟踪器
如果服务消费者需要对服务进行跟踪,比如服务何时被注册,何时被注销等可以使用服务跟踪器类主要可以跟踪到服务的注册、注销、属性修改等三个操作。
// S:追踪的服务类型;T:具体的服务对象的类型
public interface ServiceTrackerCustomizer<S, T> {
public T addingService(ServiceReference<S> reference);
public void modifiedService(ServiceReference<S> reference, T service);
public void removedService(ServiceReference<S> reference, T service);
}
addingService
添加服务时modifiedService
修改服务属性时removedService
删除服务时
使用步骤
第一步:实现一个 ServiceTrackerCustomizer 的 ServiceTrackerCustomizerImpl 类
第二步:编写 Activator
public class Activator implements BundleActivator {
private ServiceTracker<?, ?> tracker;
public void start(BundleContext context) throws Exception {
ServiceTrackerCustomizer stc = new ServiceTrackerCustomizerImpl();
tracker = new ServiceTracker<>(context, 追踪类.class.getName(), stc);
tracker.open();
}
public void stop(BundleContext context) throws Exception {
tracker.close();
}
}
3. OSGi 服务钩子
服务钩子(Service Hook)也是一种 OSGi 服务
EventListenerHook
服务的注册、注销、服务属性修改的时候,触发FindHook
服务请求时触发ListenerHook
服务监听增加或删除的时候触发
3.1 EventListenerHook
一个 Bundle 注册一个接口为 EventHook 的 Service 后,当 Framework 中有如 register,modify,unregister Service 操作时,这个勾子的 event 方法将会被调用。这个调用先于 ServiceEvent 的送出。EventHook 的 event 方法有两个参数
方法参数:
ServiceEvent event
表示将要发送的事件Map listeners
为一个 BundleContext 对象的集合,表示所有会接收此 ServiceEvent 的 Bundle
3.2 ListenerHook
一个 Bundle 注册一个接口为 ListenerHook 的 Service 后,当 Framework 中有 Service Listener 加入或删除时,会调用这个勾子的 added 或 removed 方法,这两个方法都只有一个参数
有一点需要说明的是,对于 added 方法,它在这个 ListenerHook 注册入 Framework 后会立刻被调用,从而得到在这个勾子注册之前 Framework 中已经存在的 Service Listener 对象。
方法参数:
Collection listeners
为其内部类 ListenerHook.ListernerInfo 定义的 Listener 集合。表示刚加入 Framework 或刚从 Framework 中删除的一组 Listener。同样,我们也不可以向这个 Collection 增加元素。
有一点需要说明的是,对于 added 方法,它在这个 ListenerHook 注册入 Framework 后会立刻被调用,从而得到在这个勾子注册之前 Framework 中已经存在的 Service Listener 对象。
3.3 FindHook
一个 Bundle 注册一个接口为 FindHook 的 Service 后,当 Framework 中有如 getServiceReference 等操作时,这个勾子的 find 方法将会被调用。
方法参数为:
BundleContext context
表示调用 getServiceReference 方法的 BundleContext 对象String name
表示打算寻找的 Class 的名称,null 表示寻找所有 ServiceString filter
表示打算使用的 filterboolean allServices
true表示传入的 references 参数为 getAllServiceRefereces 的结果Collection references
表示最终会返回给最开始调用如 getServiceReference 方法的 Bundle 的 Service Reference 集合
实际工作中 FindHook 比较常用,另外两种则用的很少,下面以 FindHook 演示服务钩子的使用方法:
public class Activator implements BundleActivator {
private ServiceRegistration<FindHook> serviceRegistration;
public void start(BundleContext bundleContext) throws Exception {
serviceRegistration = bundleContext.registerService(FindHook.class, new MyFindHook(), null);
}
public void stop(BundleContext bundleContext) throws Exception {
serviceRegistration.unregister();
}
}
class MyFindHook implements FindHook {
public void find(BundleContext context, String name, String filter, boolean allServices,
Collection<ServiceReference<?>> references) {
System.out.println("service hook is invoke ");
for(ServiceReference<?> sf : references) {
if("APP".equals(sf.getProperty("from"))) {
references.remove(sf);
}
}
}
}
上面的例子会将属性 from=APP 的服务过滤掉,这样 getServiceReference 时就获取不到这个服务了,起到了一个权限控制的作用。
4. OSGi 声明式服务(Declarative Service)
详情查看 felix 声明式服务官方文档
4.1 传统的服务
传统方式下,我们注册服务都是在 bundle 的激活器 (Activator) 中使用 BundleContext.registerService() 方法完成的。而服务的获取需要通过 BundleContext.getServiceReference() 获取 ServiceReference 实例,进而使用 BundleContext.getService() 得到真正的服务实例。
这种方式虽然能够完成服务的发布与使用,但是有一定的不足,具体来讲:
重复代码太多,太啰嗦。OSGi 的 bundle 是动态化的,伴随着 bundle 的安装和卸载,它所发布的服务也会动态地处于可用或不可用的状态,因此每次使用服务的时候,我们都需要借助 BundleContext 对象去服务注册中心查找,而不能通过一次查找,一劳永逸地持有服务对象的引用。尽管有 ServiceListener 和 ServiceTracker 帮助我们监听和跟踪服务的状态,但是总体而言这种方式较为繁琐且容易出错。
影响启动时间,服务在激活器中注册时,需要实例化所有要发布的服务对象,因为激活器的start()方法是同步调用的,所以会影响到整个应用的启动时间。
加大内存的占用,在激活器中注册服务时,我们需要实例化所有的服务对象,但是这些服务在应用运行期间,并不一定会用到,这在无形中加大了内存的占用。
API 依赖引起的平台侵入性。使用传统方式注册和使用服务,会用到大量的 OSGi API,从而产生与 OSGi 平台的耦合,如果要将代码复用到非 OSGi 场景之中,需要较多的重构工作。
4.2 声明式服务
OSGi 通过声明式服务(Declarative Service)以及 Blueprint 规范来解决这些问题。声明式服务基于组件模型理论,最早出现在 R4 compendium 规范之中,而 Blueprint 规范来源于 Spring Dynamic Modules 项目,最早出现于 R4.2 企业规范之中。
Declarative Service 是一个面向服务的组件模型,它制定的目的是更方便的在 OSGi 服务平台上发布,查找,绑定服务,对服务进行动态管理。
Declarative Service 采用服务组件的延迟加载以及组件生命周期管理的方式来控制对于内存的占用以及启动的速度,很好的解决了传统的OSGi 服务模型在开发和部署比较复杂的内存占用,启动慢等问题。
在 Declarative Service 中,Componect 可以是 Service 的提供者和使用者。
Declarative Service 对服务组件的描述采用结果 XML 实现。
4.2 实战演示
(1) 新建 2 个 bundle,目录结构如下:
(2) 编写 declare-service 服务类
第一步:编写服务类
package com.github.binarylei;
public class RunableService implements Runnable {
@Override
public void run() {
System.out.println("service run...");
}
}
第二步:编写 declare.xml 配制文件:
<?xml version="1.0" encoding="UTF-8"?>
<component name="declare.service" immediate="true"
xmlns="http://www.osgi.org/xmlns/scr/v1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.2.0 https://osgi.org/xmlns/scr/v1.2.0/scr.xsd">
<implementation class="com.github.binarylei.RunableService" />
<property name="service.description" value="Declarative Service" />
<property name="service.vendor" value="Apache" />
<service>
<provide interface="java.lang.Runnable" />
</service>
</component>
第三步:将 declare.xml 配制文件添加到 META-INF/MANIFEST.MF 中
<!--Service-Component: declare.xml-->
<Service-Component>declare.xml</Service-Component>
(3) declare-client 与 declare-service 类似
第一步:编写服务类
package com.github.binarylei;
import java.util.Map;
public class MyClient {
public void bind(Runnable service, Map<?, ?> properties) {
System.out.println("start...");
service.run();
}
public void unbind(Runnable service, Map<?, ?> properties) {
System.out.println("end...");
service.run();
}
}
第二步:编写 declare.xml 配制文件:
<?xml version="1.0" encoding="UTF-8"?>
<component name="declare.client" immediate="true"
xmlns="http://www.osgi.org/xmlns/scr/v1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.2.0 https://osgi.org/xmlns/scr/v1.2.0/scr.xsd">
<implementation class="com.github.binarylei.MyClient" />
<reference interface="java.lang.Runnable" bind="bind" unbind="unbind"/>
</component>
第三步:将 declare.xml 配制文件添加到 META-INF/MANIFEST.MF 中
<!--Service-Component: declare.xml-->
<Service-Component>declare.xml</Service-Component>
(4) 运行 felix
要想在 felix 使用 Declarative Service ,先到 http://felix.apache.org/downloads.cgi 下载 SCR(Declarative Services)
测试一下哟!
OSGi 系列(七)之服务的监听、跟踪、声明等的更多相关文章
- Socket(TCP)客户端请求和服务端监听和链接基础(附例子)
一:基础知识回顾 一: Socket 类 实现 Berkeley 套接字接口. Socket(AddressFamily, SocketType,ProtocolType) 使用指定的地址族.套接字类 ...
- Linux下启动Oracle服务和监听程序步骤
Linux下启动Oracle服务和监听程序启动和关闭步骤整理如下: 1.安装oracle: 2.创建oracle系统用户: 3./home/oracle下面的.bash_profile添加几个环境变量 ...
- laravel进阶系列--通过事件和事件监听实现服务解耦
简介 Laravel 事件提供了简单的观察着模式实现,允许你订阅和监听应用中的事件.事件类通常存放在 app/Events 目录. 监听器存放在 app/Listeners. 如果你在应用中没有看到这 ...
- linux上使用netstat查看当前服务和监听端口
netstat这个命令常用在网络监控方面.利用这个命令,可以查看当前系统监听的服务和已经建立的服务,以及相应的端口.协议等信息. netstat参数说明 netstat参数虽然很多,但是常用的不多,主 ...
- linux用netstat查看服务及监听端口
[root@localhost ~]# netstat -nlp netstat命令各个参数说明如下: -t : 指明显示TCP端口 -u : 指明显示UDP端口 -l : 仅显示监听套接字(所谓套接 ...
- nodejs中创建web服务,监听本地IP
nodejs官网例子 var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {' ...
- Windows API 教程(七) hook 钩子监听
茵蒂克丝 如何创建一个窗口 手动创建窗口的流程 实际代码 安装钩子 (Install hook) 钩子简介 SetWindowsHookEx 函数 设置监听[键盘]消息 设置监听[鼠标]消息 如何创建 ...
- cmd启动Oracle服务和监听服务
启动数据库服务 net start oracleserviceorcl 启动数据库监听 lsnrctl start
- Linux下的启动oracle服务 启动监听 开放端口操作
尝试登录oracle 使用root用户将没有sqlplus命令 [root@localhost ~]# sqlplus /nolog bash: sqlplus: 未找到命令... [root ...
随机推荐
- jQuery实现todo及轮播图
内容: 1.todo程序 2.轮播图 1.todo程序 需求: 实现一个todo程序,可以添加数据,可以删除数据,可以修改数据,可以查看所有数据 另外实现自己的一系列弹窗:用于提示用户的提示框.用于警 ...
- uva-10129-欧拉通路
题意:每一个单词的长度最小2,最大1000,单词开头的字母和另外一个单词的末尾一样就可以连接起来,解所有的单词是不是都可以连接起来,没有遗漏的 把每一个单词的第一个字母当成一个结点,最后一个单词也作为 ...
- 1.纯 CSS 创作一个按钮文字滑动特效 + 弹幕(残缺)
原文地址:1# 视频演示如何用纯 CSS 创作一个按钮文字滑动特效 扩展后地址:https://scrimba.com/c/cJkzMfd HTML代码: <html> <head& ...
- OpenCL 双调排序 CPU 版
▶ 学习了双调排序,参考(https://blog.csdn.net/xbinworld/article/details/76408595) ● 使用 CPU 排序的代码 #include <s ...
- distinct group by
select num from test_test group by num; 比 select distinct(num) from test_test; 效率高 select count(dis ...
- aix系统使用随笔
在 Aix操作系统 中,常用的文档编辑命令是 vi.下面,我们就来学习一下有关vi的使用决窍. 在vi中,必须牢记它是有两个状态的 ---- 输入状态与命令状态.由输入状态切换 到命令状态,必须ESC ...
- Devexpress 百分号显示格式
百分号:{0:P}表示显示为百分号模式.如数据源中为0.5.表示出来为50%
- delphi 编译生成ipa文件 adhoc步骤
找IPA文件 开发模式ipa文件和发布模式ipa文件,路径不同. http://www.itnose.net/detail/6101808.html 一.开发模式Development 不需要真机,可 ...
- delphi 泛型 c++builder 泛型
delphi 泛型 System.Generics.Collections.pas TList<T> http://docwiki.embarcadero.com/Libraries/Be ...
- ios 避免navigationcontroller出现时scrollview内容被resize
viewDidLoad中设置以下属性 self.automaticallyAdjustsScrollViewInsets = NO;