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. 7. mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    转自:http://www.kyjszj.com/htzq/79.html 1. if 语句 (简单的条件判断) 2. choose (when,otherwize) ,相当于java 语言中的 sw ...

  2. json和java bean的相互转换(使用fastjson)

    <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifac ...

  3. as3 优化

    1 代码写法 1 定义局部变量 定义局部变量的时候,一定要用关键字var来定义,因为在Flash播放器中,局部变量的运行速度更快,而且在他们的作用域外是不耗占系统资源的.当一个函数调用结束的时候,相应 ...

  4. Simple2D-18(音乐播放器)使用 bass 音频库

    BASS 简介 BASS是一个在多个平台上用于软件的音频库.其目的是为开发人员提供功能强大且高效的示例流(MP3,MP2,MP1,OGG,WAV,AIFF),MOD 音乐(XM,IT,S3M,MOD ...

  5. tensorflow ValueError: Cannot feed value of shape (5000,) for Tensor 'output:0', which has shape '(?, 10)'

    提供的训练数据和定义的模型之间的维度不对应. 在MNIST手写数字识别时,在 mnist = input_data.read_data_sets("MNIST_data/") 中, ...

  6. eclipse 注释字体不一致的问题

    eclipse中 1.解决注释的文字大小不一的情况 2.想让注释和代码大小不一样 3.win10系统下,设置Text Font时找不到Courier New字体 1.解决注释的文字大小不一的情况 打开 ...

  7. SpringMVC知识(1)

    1.SpringMVC的工作流程 流程 : 1.用户发送请求至前端控制器DispatcherServlet 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器. ...

  8. 编写jQuery插件(一)——插件约定及插件中的闭包

    编写插件的目的是给已经有的一系列方法或函数做一个封装,以便在其他地方重复使用,提高开发效率和方便后期维护. 在编写jQuery插件的时候,我们一般会遵循一些约定: jQuery插件推荐命名为:jque ...

  9. 吴裕雄 实战PYTHON编程(4)

    import hashlib md5 = hashlib.md5()md5.update(b'Test String')print(md5.hexdigest()) import hashlib md ...

  10. 神经网络中embedding层作用——本质就是word2vec,数据降维,同时可以很方便计算同义词(各个word之间的距离),底层实现是2-gram(词频)+神经网络

    Embedding tflearn.layers.embedding_ops.embedding (incoming, input_dim, output_dim, validate_indices= ...