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. UVA327

    模拟 这个问题的任务是求解一组c语言里的表达式,但是你不需要知道c语言是怎么解决这个问题!每一行一个表达式,每个表达式的组成不会超过110个字符.待求解的表达式只包含一个int类型变量和一个组有限的操 ...

  2. sbt的安装测试

    1.下载 wget https://github.com/sbt/sbt/releases/download/v0.13.15/sbt-0.13.15.tgz 2.安装 tar -zxvf sbt-0 ...

  3. leetcode520

    public class Solution { public bool DetectCapitalUse(string word) { var length = word.Length; ) { ; ...

  4. 5.log4j报错

    java.lang.UnsupportedClassVersionError: org/apache/log4j/Logger : Unsupported major.minor version 51 ...

  5. word 2013 自动保存太慢,下面读条起码3分钟

    该问题有可能是应用干扰或者安全设置问题导致的. 建议您尝试以下方法: 方法一: 尝试使用干净启动来暂时禁用计算机启动时所加载的第三方程序来进一步做测试: 如何在Windows 中执行干净启动步骤 具体 ...

  6. 创建一个多进程(multiprocessing.Process)

    进程是资源的集合,每个进程至少包含一个线程 import multiprocessing #导入进程模块import time, threading #导入线程 def thread_run(): p ...

  7. Java HashMap两种遍历方式

    第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...

  8. LVS原理以及配置

    安装好ipvsadm后需要查看内核是否加载了ip_vs模块儿,如果没有需要手动执行ipvsadm进行加载: # ipvsadm # lsmod |grep ip_vs # rmmod ip_vs_rr ...

  9. spring浏览器国际化的原理

    We will add two languages support to our application: English and German. Depending on the locale se ...

  10. org.springframework.stereotype 注解

    org.springframework.stereotype 1.@controller 控制器(注入服务) 2.@service 服务(注入dao) 3.@repository dao(实现dao访 ...