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 ...
随机推荐
- os内置模块
import os 1.os.getcwd() # 获得当前文件路径 2.os.chdir() # 改变当前目录 3.os.curdir # . 表示当前目录 4.os.pardir # 表示上 ...
- Mysql 游标使用
BEGIN #shopsId 商家ID #accountDay 10位日期 -- 定义一个或者多个 变量来接收 游标查询的列值 DECLARE receiptContentId INT; -- 遍历数 ...
- clamav杀毒软件的安装
Linux Unix.Trojan.Elknot (Linux.BackDoor.Gates.5)木马清理 此恶意软件结合了传统后门程序和DDoS攻击木马的功能 前两天性能测试服务器被种马,cpu一直 ...
- 不用登陆密码也能进路由器,适用于TP、磊科、腾达
结合wooyun提供的腾达COOKIE漏洞,结合自己的经验,成功进入腾达路由器破解其登陆密码和无线密码. 教程开始: 所用工具:WebCruiser 输入路由网关,出现登陆界面. 选择:COOKIE ...
- 迷你MVVM框架 avalonjs 学习教程12、数据联动
在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的.在avalon里,存在各种绑定回调与$watch回调,完全满足 ...
- Ubuntu下面网络固定ip
https://jingyan.baidu.com/article/e5c39bf5bbe0e739d7603396.html
- Python sum() 函数
Python sum() 函数 Python 内置函数 描述 sum() 方法对系列进行求和计算. 语法 以下是 sum() 方法的语法: sum(iterable[, start]) 参数 ite ...
- 使用Sql分页方法给Repeater控件分页的方法
页面代码 <div class="bookList"> <asp:Repeater ID="rpBooks" runat="serv ...
- python之字符串【str】
#Auther Bob#--*--conding:utf-8 --*-- #定义一个str的对象,有下面两种方法name = 'Bob abc'job = str('it')print(type(na ...
- Java注解(Annotation)用法:利用注解和反射机制指定列名导出数据库数据
闲来没事,想了一个应用的例子:用java如何把数据库的数据根据我们指定的某几列,如第2列,第4列,第6列导出来到Excel里? 写代码也是为了应用的,写好的代码更重要的是在于思考.我自己思考了这个示例 ...