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,使用服务工厂好处:

  1. 有时 service 需要知道是哪个 bundle 在使用它。例如 logger 服务,它需要在日志中记录是哪个 bundle 调用它的。
  2. 延迟初始化 Service
  3. 这对消费者是透明的,它不能知道提供服务的是普通 Service 还是 ServiceFactory
  4. 可以创建多种服务,根据参数 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 系列(六)之服务的使用的更多相关文章

  1. jvm系列(六):Java服务GC参数调优案例

    本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...

  2. OSGi 系列(十六)之 JDBC Service

    OSGi 系列(十六)之 JDBC Service compendium 规范提供了 org.osgi.service.jdbc.DataSourceFactory 服务 1. 快速入门 1.1 环境 ...

  3. OSGi 系列(七)之服务的监听、跟踪、声明等

    OSGi 系列(七)之服务的监听.跟踪.声明等 1. OSGi 服务的事件监听 和 bundle 的事件监听类似,服务的事件监听是在服务注册.注销,属性被修改的时候,OSGi 框架会发出各种不同的事件 ...

  4. Dubbo系列之 (六)服务订阅(3)

    辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...

  5. WCF编程系列(六)以编程方式配置终结点

    WCF编程系列(六)以编程方式配置终结点   示例一中我们的宿主程序非常简单:只是简单的实例化了一个ServiceHost对象,然后调用open方法来启动服务.而关于终结点的配置我们都是通过配置文件来 ...

  6. OSGi 系列(十二)之 Http Service

    OSGi 系列(十二)之 Http Service 1. 原始的 HttpService (1) 新建 web-osgi 工程,目录结构如下: (2) HomeServlet package com. ...

  7. OSGi 系列(十三)之 Configuration Admin Service

    OSGi 系列(十三)之 Configuration Admin Service OSGi 的 CM 就是 Configuration Admin Service,是用于管理 Bundle 属性.并在 ...

  8. OSGi 系列(十四)之 Event Admin Service

    OSGi 系列(十四)之 Event Admin Service OSGi 的 Event Admin 服务规范提供了开发者基于发布/订阅模型,通过事件机制实现 Bundle 间协作的标准通讯方式. ...

  9. OSGi 系列(十)之 Blueprint

    OSGi 系列(十)之 Blueprint blueprint 是 OSGi 的一个规范,类似于 spring 的 IOC,用来处理 OSGi 的动态特性,可以大大简化服务的使用. blueprint ...

随机推荐

  1. python拓展1 week1-week5复习回顾

    知识内容: 1.python基础概念及基础语法 2.python基础数据类型 3.python模块相关 4.python函数相关 5.python面向对象相关 6.python文件处理相关 注:本节内 ...

  2. 分水岭算法(理论+opencv实现)

    分水岭算法理论 从意思上就知道通过用水来进行分类,学术上说什么基于拓扑结构的形态学...其实就是根据把图像比作一副地貌,然后通过最低点和最高点去分类! 原始的分水岭: 就是上面说的方式,接下来用一幅图 ...

  3. 本地yum源快速创建

    1.建立挂载目录mkdir /rui 2.挂载iso到新建的/rui目录

  4. wzben的QQ空间

    实习之后没有动过博客了,后续慢慢补.

  5. leetcode166

    public class Solution { public String fractionToDecimal(int numerator, int denominator) { HashMap< ...

  6. J2SE 8的Lambda --- 语法

    语法例子 LambdaGrammarTest lambdaTest = new LambdaGrammarTest(); // 1. 能够推导出类型的,可以不写类型 String[] planets ...

  7. ant 注意

    nt文件在部署时,如果控制台出现乱码则需要调整语言. 高版本eclipse在jdk高版本中已经植入了ant的部署.因此不需要单独配置ant.jar. 如果版本低,可下载ant插件,或者下载ant的工具 ...

  8. SRC是在本位置显示:source的缩写,源的意思 HREF是点击后连接的目标:HyperlinkReference,超链接引用

    SRC是在本位置显示:source的缩写,源的意思HREF是点击后连接的目标:HyperlinkReference,超链接引用

  9. 转。。原理同样支持 delphi

    我用C#导出excel 带图片,用office2003 正常,但换成office 2007 时,我指定多个单元格分别插入图片,这个图片不在此单元格内,这些图片全都集中在一起,在一个位置上.很奇怪,看起 ...

  10. MySQL 逻辑备份工具

    简介: Mydumper.Myloader 是一个第三方的.开源的 MySQL 逻辑备份工具. 支持多线程,比起 mysqldump 要快很多,也能解决 innobackupex 备份工具对 MyIS ...