OSGi 系列(六)之服务的使用
OSGi 系列(六)之服务的使用
1. 为什么使用服务
- 降低服务提供者和服务使用者直接的耦合,这样更容易重用组件
- 隐藏了服务的实现细节
- 支持多个服务的实现、这样你可以互换这实现
2. 服务的使用
2.1 服务的注册
bundle 通过在框架的服务注册中心注册一个服务对象来发布一个服务。安装在 OSGi 环境下的其它 bundle 就可以访问到在框架中注册的服务对象。
bundle 通过使用 BundleContext.registerService,在框架中注册一个服务对象:
registerService(String, Object, Dictionary) //用于一个服务接口的服务注册
registerService(String[], Object, Dictionary) //用于多个服务接口的服务注册
String表示服务的接口Object表示服务的实现类Dictionary表示服务属性- 调用之后,返回 ServiceRegistration 对象
2.2 服务的销毁
ServiceRegistration.unregister()
2.3 服务的属性
| 属性 | 类型 | 常量 | 描述 |
|---|---|---|---|
| objectClass | String[] | OBJECTCLASS | objectClass 属性包含了注册到框架中的服务对象所有实现的接口的集合。这个属性必须由框架自动设置 |
| service.id | Long | SERVICE_ID | 每一个注册了的服务对象都由框架分配了一个惟一的service.id。将这个标志数字添加到服务对象的属性中。框架给每一个注册的服务对象分配一个惟一的标志值,这个值要比原来分配的任何值要大,也就是说是递增分配的。 |
| service.pid | String | SERVICE_PID | service.pid属性是可选的,标记了服务对象的持久惟一标记。 |
| service.ranking | Integer | SERVICE_RANKING | 服务的排行,当有多个服务的时候,会返回service.ranking值最大的那个服务 |
| service.description | String | SERVICE_DESCRIPTION | service.description属性用于文档性的描述,这个属性是可选的 |
| service.vendor | String | SERVICE_VENDOR | 这是一个可选属性,描述服务对象的开发商信息 |
2.4 服务的查找
查找服务时要分两步:
bundleContext.getServiceReference() //1. 获取 ServiceReference 对象
bundleContext.getService() //2. 获取真实的服务对象
查找单个服务:
bundleContext.getServiceReference() 要么返回 null,要么返回一个服务。如果有多个服务匹配,也只会返回一个服务。
- 找 service.ranking 属性最高的。如果注册时为指定该属性,则默认值为0
- 找 service ID 属性最小的。也就是最先注册的服务。
查找多个服务:
bundleContext.getServiceReferences(Clazz clazz, String filter)
bundleContext.getServiceReferences(String clazz, String filter)
第二个参数接受标准的LDAP过滤字符串。示例:
属性匹配:(vendor=Apache)、(count>3)
通配符:(vendor=Apache*)
判断某个属性是否存在:(vendor=)
条件非:(!(vendor=Apache))
条件与:(&(objectClass=com.edu.osgi.user.IUserService)(type=1))
条件或:(|(type=1)(type=2))
3.实战演示
3.1 新建 4 个 bundle,目录结构如下:

3.2 email-api 为接口
package com.github.binarylei.email.api;
public interface EmailService {
void sendEmail(String to, String title, String content);
}
注意: email-api 要将接口暴露出去,配制如下:
<Import-Package>org.osgi.framework</Import-Package>
<Export-Package>com.github.binarylei.email.api</Export-Package>
3.3 email-service-139 实现
package com.github.binarylei.email.service;
import com.github.binarylei.email.api.EmailService;
public class EmailService139 implements EmailService {
public void sendEmail(String dest, String title, String content) {
System.out.println("139 email send. dest=" + dest + ",title=" + title + ",content=" + content);
}
}
BundleActivator 如下:
package com.github.binarylei.email.internal;
import com.github.binarylei.email.api.EmailService;
import com.github.binarylei.email.service.EmailService139;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
public class Activator implements BundleActivator {
ServiceRegistration<EmailService> serviceRegistration;
@Override
public void start(BundleContext context) throws Exception {
serviceRegistration = context.registerService(EmailService.class, new EmailService139(), null);
}
@Override
public void stop(BundleContext context) throws Exception {
serviceRegistration.unregister();
}
}
注意: email-service-139 要将引入 email-api 接口,配制如下:
<Import-Package>org.osgi.framework,com.github.binarylei.email.api</Import-Package>
<Bundle-Activator>com.github.binarylei.email.internal.Activator</Bundle-Activator>
3.4 email-service-163 实现
与 email-service-139 类似
3.5 email-client 服务的使用
package com.github.binarylei.email.internal;
import com.github.binarylei.email.api.EmailService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
//1. 获取所有的服务
ServiceReference<?>[] refs = context.getAllServiceReferences(EmailService.class.getName(), null);
if (refs != null) {
for (ServiceReference ref : refs) {
EmailService emailService = (EmailService) context.getService(ref);
emailService.sendEmail("binarylei@qq.com", "OSGi", "OSGi Service");
}
}
//2. 获取单个服务
ServiceReference<?> ref = context.getServiceReference(EmailService.class.getName());
if (ref != null) {
EmailService emailService = (EmailService) context.getService(ref);
emailService.sendEmail("binarylei@qq.com", "OSGi", "OSGi Service");
}
}
@Override
public void stop(BundleContext context) throws Exception {
}
}
注意: email-client 要将引入 email-api 接口,配制如下:
<Import-Package>org.osgi.framework,com.github.binarylei.email.api</Import-Package>
<Bundle-Activator>com.github.binarylei.email.internal.Activator</Bundle-Activator>
3.6 felix 测试
将这 4 个 bundle 拷贝到 felix-framework-5.6.10/bundle 下,启动 felix

4. 服务的属性使用
4.1 服务添加属性
分别给 email-service-163 和 email-service-139 添加属性 vendor
@Override
public void start(BundleContext context) throws Exception {
Dictionary properties = new Hashtable<>();
properties.put("vendor", "163"); // 139
serviceRegistration = context.registerService(EmailService.class, new EmailService163(), properties);
}
4.2 根据服务属性获取对应的服务
email-client 获取属性
@Override
public void start(BundleContext context) throws Exception {
// 根据属性获取163的服务
ServiceReference<?>[] refs = context.getServiceReferences(EmailService.class.getName(), "(vendor=163)");
if (refs != null) {
for (ServiceReference ref : refs) {
EmailService emailService = (EmailService) context.getService(ref);
emailService.sendEmail("binarylei@qq.com", "OSGi", "OSGi Service");
}
}
}
5. 服务工厂
使用传统的方式获取的服务都是单例的,同一个服务,不管是在同一个 bundle, 还是在不同 bundle 中间获取。
5.1 测试单例
将 email-client-1.0.0.jar 复制一份 email-client-2.0.0.jar,修改 META-INF/MANIFEST.MF 的 Bundle-Version 为 2.0.0

5.2 ServiceFactory
org.osgi.framework.ServiceFactory,使用服务工厂好处:
- 有时 service 需要知道是哪个 bundle 在使用它。例如 logger 服务,它需要在日志中记录是哪个 bundle 调用它的。
- 延迟初始化 Service
- 这对消费者是透明的,它不能知道提供服务的是普通 Service 还是 ServiceFactory
- 可以创建多种服务,根据参数 ServiceRegistration 来判断
5.3 实例
(1) email-service-163 添加 EmailServiceFactory 类:
package com.github.binarylei.email.service;
import com.github.binarylei.email.api.EmailService;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
public class EmailServiceFactory implements ServiceFactory<EmailService> {
@Override
public EmailService getService(Bundle bundle, ServiceRegistration<EmailService> registration) {
return new EmailService163();
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration<EmailService> registration, EmailService service) {
}
}
(2) 修改 Activator 类:
@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);
}
(3) 更新 email-service-163,重启 email-client-1.0.0.jar 和 email-client-2.0.0.jar ,结果如下:

6. OSGi 服务
核心服务:包管理、启动级别、权限管理、URL处理
compendium 服务:
- LOG Service(日志服务)
- HTTP Service(注册servlet和资源)
- Configuration Admin(配置管理)
- Event Admin(事件通知)
- Declarative Services(定义轻量级的面向服务的组件模型)
- Blueprint(一个类似 IOC 容器的实现)
OSGi 系列(六)之服务的使用的更多相关文章
- jvm系列(六):Java服务GC参数调优案例
本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...
- OSGi 系列(十六)之 JDBC Service
OSGi 系列(十六)之 JDBC Service compendium 规范提供了 org.osgi.service.jdbc.DataSourceFactory 服务 1. 快速入门 1.1 环境 ...
- OSGi 系列(七)之服务的监听、跟踪、声明等
OSGi 系列(七)之服务的监听.跟踪.声明等 1. OSGi 服务的事件监听 和 bundle 的事件监听类似,服务的事件监听是在服务注册.注销,属性被修改的时候,OSGi 框架会发出各种不同的事件 ...
- Dubbo系列之 (六)服务订阅(3)
辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...
- WCF编程系列(六)以编程方式配置终结点
WCF编程系列(六)以编程方式配置终结点 示例一中我们的宿主程序非常简单:只是简单的实例化了一个ServiceHost对象,然后调用open方法来启动服务.而关于终结点的配置我们都是通过配置文件来 ...
- OSGi 系列(十二)之 Http Service
OSGi 系列(十二)之 Http Service 1. 原始的 HttpService (1) 新建 web-osgi 工程,目录结构如下: (2) HomeServlet package com. ...
- OSGi 系列(十三)之 Configuration Admin Service
OSGi 系列(十三)之 Configuration Admin Service OSGi 的 CM 就是 Configuration Admin Service,是用于管理 Bundle 属性.并在 ...
- OSGi 系列(十四)之 Event Admin Service
OSGi 系列(十四)之 Event Admin Service OSGi 的 Event Admin 服务规范提供了开发者基于发布/订阅模型,通过事件机制实现 Bundle 间协作的标准通讯方式. ...
- OSGi 系列(十)之 Blueprint
OSGi 系列(十)之 Blueprint blueprint 是 OSGi 的一个规范,类似于 spring 的 IOC,用来处理 OSGi 的动态特性,可以大大简化服务的使用. blueprint ...
随机推荐
- Selenium如何定位动态id的元素?
怎么定位这类型的元素呢? 根据其他属性定位如果有其他固定属性,最先考虑的当然是根据元素的其他属性来定位,定位方式那么多,何必在这一棵树上吊死.. 根据相对关系定位根据其附近的父节点.子节点.兄弟节点定 ...
- leetcode754
class Solution { public: int reachNumber(int target) { // 理解这题的意思 这题就好做了 // 分析 首先考虑一种比较极端的情况 即一直向正方向 ...
- 更改maven下载jar的仓库为阿里云仓库
修改settings.xml <!-- 配置本地maven的仓库 --> <localRepository>D:\file\path\maven\repository</ ...
- hibernate注解主键生成策略
Id生成策略: @GeneratedValue,JPA通用策略生成器 . JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO. TABLE:使用一个特定的数据库表格来 ...
- 了解innodb_support_xa(分布式事务)
innodb_support_xa可以开关InnoDB的xa两段式事务提交.默认情况下,innodb_support_xa=true,支持xa两段式事务提交.此时MySQL首先要求innodb pre ...
- 把AVI存在资源中用TAnimate播放
Animate1.RESName := 'About'; Animate1.Active := True;
- C++的空指针、野指针和指针赋值NULL.md
1.空指针和野指针 http://blog.csdn.net/fu_zk/article/details/21030607 空指针常量 一个表示0值的整数常量,叫做空指针常量.例如:0.0L.1-1( ...
- pyplot文本显示
pyplot文本显示 pyplot中文字符显示 pyplot默认不支持中文字符,因为默认字体是sans-serif,英文字体不能显示中文 方法1,修改需要输出中文字符的地方 在有中文输出的地方,添加属 ...
- SMO算法(转)
作者:[已重置]链接:https://www.zhihu.com/question/40546280/answer/88539689来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...
- jquery读取本地文件,Windows上报错。XMLHttpRequest cannot load xxx. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.k.cors.a.c
问题: 测试报告,使用本地的json.txt文件,结果文件读取失败,报错如下: XMLHttpRequest cannot load xxx. Cross origin requests are on ...