12.3. 导入服务

概述

本节论述了如何获取和使用被导出到 OSGi 服务 registry 的 OSGi 服务的引用。您可以使用 引用 元素或 参考列表 元素导入 OSGi 服务。reference 元素适合访问 无状态 服务,而 参考列表 元素则适合访问 有状态 服务。

管理服务引用

支持获取 OSGi 服务参考的以下模型:

参考管理器

reference manager 实例由 Blueprint 参考 元素创建。此元素返回单个服务引用,它是访问 无状态 服务的首选方法。图 12.1 “对无状态服务的引用” 展示了使用参考管理器访问无状态服务的模型概述。

图 12.1. 对无状态服务的引用

部署简单的 svc 01

客户端 Blueprint 容器中的 Bean 使用代理对象( 提供的对象 )注入,该对象由 OSGi 服务注册表的服务对象( 后备服务)支持。这个模型通过以下方式明确利用无状态服务可交换的事实:

  • 如果发现多个服务实例与 参考 元素中的条件匹配,则参考管理器可以任意选择其中一个作为后备实例(因为可以交换它们)。
  • 如果后备服务消失,则参考管理器可以立即切换为使用同一类型的其它可用服务之一。因此,不能保证,从一种方法调用下一个方法,代理会保持连接到同一后端服务。

客户端和后备服务之间的合同 是无状态的,客户端 不得 假定它始终与同一服务实例通信。如果没有可用的匹配的服务实例,代理会在引发 ServiceUnavailable 异常前等待特定的时间。可以通过设置 reference 元素上的 timeout 属性来配置超时的长度。

参考列表管理器

Blueprint reference-list 元素会创建一个参考列表管理器 实例。此元素返回服务引用列表,它是访问 有状态 服务的首选方法。图 12.2 “Stateful 服务的参考列表” 展示了使用参考列表管理器访问有状态服务的模型概述。

图 12.2. Stateful 服务的参考列表

部署简单的 svc 02

客户端蓝图容器中的 Bean 通过 java.util.List 对象( 提供的对象)注入,其中包含代理对象列表。每个代理都由 OSGi 服务 registry 中的唯一服务实例来支持。与无状态模型不同,支持服务 不被视为可交换的。实际上,列表中每个代理的生命周期都与对应的支持服务的生命周期紧密关联:当服务在 OSGi 注册表中注册时,会同步创建并添加到代理列表中;在服务从 OSGi 注册表中取消注册时,相应的代理会同步从代理列表中删除。

代理及其支持服务之间的合同 是有状态的,因此当它在特定代理上调用方法时,客户端可能会假定它始终 与同一 后端服务通信。但是,如果后备服务不可用,但可能会在代理变得过时时,该后备服务可能会变得不可用。在过时的代理上调用方法的任何尝试都会生成 ServiceUnavailable 异常。

通过接口(无状态)匹配.

获取 状态 服务引用最简单的方法是使用 参考 元素上的 interface 属性来指定要匹配的 接口。如果 interface 属性值是该服务的超类型,或者属性值是由服务实施的 Java 接口( interface 属性可指定 Java 类或 Java 接口),则服务被认为是匹配的。

例如,若要引用无状态保存 帐户 服务(请参阅 例 12.1 “使用单一接口进行服务导出示例”),请按如下所示定义一个 参考 元素:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <reference id="savingsRef"
             interface="org.fusesource.example.SavingsAccount"/>

  <bean id="client" class="org.fusesource.example.client.Client">
    <property name="savingsAccount" ref="savingsRef"/>
  </bean>

</blueprint>

如果 参考 元素创建了一个参考管理器,其 ID 为 savingsRef。要使用被引用的服务,请将 savings Refan 注入到您的其中一个客户端类中,如下所示。

注入客户端类的 bean 属性可以是可在保存账户中分配的任何类型。例如,您可以按照以下方式定义 Client 类:

package org.fusesource.example.client;

import org.fusesource.example.SavingsAccount;

public class Client {
    SavingsAccount savingsAccount;

    // Bean properties
    public SavingsAccount getSavingsAccount() {
        return savingsAccount;
    }

    public void setSavingsAccount(SavingsAccount savingsAccount) {
        this.savingsAccount = savingsAccount;
    }
    ...
}

通过接口(stateful)进行匹配.

获取 有状态 服务引用的最简单方法是通过利用 reference-list 元素上的 interface 属性来指定要匹配的接口。然后,参考列表管理器会获取所有服务的列表,其接口 属性值是服务的超类型,或者由服务实施的 Java 接口( 接口 属性可以指定 Java 类或 Java 接口)。

例如,若要引用有状态 savings sAccount 服务(请参阅 例 12.1 “使用单一接口进行服务导出示例”),请定义 参考列表 元素,如下所示:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <reference-list id="savingsListRef"
                  interface="org.fusesource.example.SavingsAccount"/>

  <bean id="client" class="org.fusesource.example.client.Client">
    <property name="savingsAccountList" ref="savingsListRef"/>
  </bean>

</blueprint>

其中 reference-list 元素会创建一个参考列表管理器及 ID, savingsListRef。要使用引用的服务列表,请将 savings ListRefan 引用注入到您的其中一个客户端类中,如下所示。

默认情况下,savingsAccountList bean 属性是一个服务对象列表(例如 java.util.List<SavingsAccount>)。您可以按照以下方法定义客户端类:

package org.fusesource.example.client;

import org.fusesource.example.SavingsAccount;

public class Client {
    java.util.List<SavingsAccount> accountList;

    // Bean properties
    public java.util.List<SavingsAccount> getSavingsAccountList() {
        return accountList;
    }

    public void setSavingsAccountList(
                    java.util.List<SavingsAccount> accountList
    ) {
        this.accountList = accountList;
    }
    ...
}

根据接口和组件名称匹配

要匹配 无状态 服务的接口和组件名称(bean ID),请指定 参考 元素上的 interface 属性和 component-name 属性,如下所示:

<reference id="savingsRef"
           interface="org.fusesource.example.SavingsAccount"
           component-name="savings"/>

要匹配 有状态 服务的接口和组件名称(bean ID),请同时指定 reference-list 元素上的 interface 属性和 component-name 属性,如下所示:

<reference-list id="savingsRef"
           interface="org.fusesource.example.SavingsAccount"
           component-name="savings"/>

使用过滤器匹配服务属性

您可以通过匹配过滤器的服务属性来选择服务。过滤器通过引用元素或 reference -list 元素上的 filter 属性来指定。filter 属性的值必须是 LDAP 过滤器表达式。例如,要定义与 bank.name 服务属性等于 HighStreetBank 时匹配的过滤器,您可以使用以下 LDAP 过滤器表达式:

(bank.name=HighStreetBank)

要匹配两个服务属性值,您可以使用 和 结合使用表达式和逻辑 。例如,要要求 foo 属性等于 FooValue,而 bar 属性等于 BarValue,您可以使用以下 LDAP 过滤器表达式:

(&(foo=FooValue)(bar=BarValue))

有关 LDAP 过滤器表达式的完整语法,请参阅 OSGi 核心规格的 3.2.7 节

过滤器也可以与 接口和 组件名称 设置结合使用,在这种情况下,所有指定条件都需要匹配。

例如,若要将 无状态 服务保存 类型匹配,其 银行 服务属性等于 HighStreetBank,您可以定义一个 参考 元素,如下所示:

<reference id="savingsRef"
           interface="org.fusesource.example.SavingsAccount"
           filter="(bank.name=HighStreetBank)"/>

要匹配 有状态的 服务 保存账户 类型,银行.name 服务属性等于 HighStreetBank,您可以定义 参考列表 元素,如下所示:

<reference-list id="savingsRef"
           interface="org.fusesource.example.SavingsAccount"
           filter="(bank.name=HighStreetBank)"/>

指定是否强制(必需)或可选

默认情况下,LOSOS 服务的引用被假定为强制(请参阅 必需的依赖项)。通过在元素上设置 availability 属性,可以自定义参考元素的依赖关系行为或 reference -list 元素。

availability 属性有两个可能的值:

  • 强制 (默认)意味着在普通的 Blueprint 容器初始化过程中 必须解决 依赖项
  • 可选,这意味着在初始化过程中 不需要 解析依赖项。

以下 参考 元素示例显示了如何明确声明引用是一个强制依赖项:

<reference id="savingsRef"
           interface="org.fusesource.example.SavingsAccount"
           availability="mandatory"/>

指定引用监听程序

例如,若要应对 OSGi 环境的动态性质(例如,如果您声明了一些服务引用)具有 可选 可用性,在将后备服务绑定到注册表并且其未绑定时,跟踪服务通常很有用。要接收服务绑定和取消绑定事件的通知,您可以将 reference-listener 元素定义为参考元素或 reference -list 元素的子项。

例如,以下 Blueprint 配置演示了如何将引用监听程序定义为 ID,savingsRef:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <reference id="savingsRef"
             interface="org.fusesource.example.SavingsAccount"
             >
    <reference-listener bind-method="onBind" unbind-method="onUnbind">
      <bean class="org.fusesource.example.client.Listener"/>
    </reference-listener>
  </reference>

  <bean id="client" class="org.fusesource.example.client.Client">
    <property name="savingsAcc" ref="savingsRef"/>
  </bean>

</blueprint>

上述配置将 org.fusesource.example.client.Listener 类型的实例注册为侦听 bindunbind 事件的回调。每当 savingsRef 引用管理器的后备服务绑定或未绑定时,都会生成事件。

以下示例显示了 Listener 类的实现示例:

package org.fusesource.example.client;

import org.osgi.framework.ServiceReference;

public class Listener {

    public void onBind(ServiceReference ref) {
        System.out.println("Bound service: " + ref);
    }

    public void onUnbind(ServiceReference ref) {
        System.out.println("Unbound service: " + ref);
    }

}

方法名称 onBindonUnbind 分别通过 bind-methodunbind-method 属性指定。这两个回调方法都使用 org.osgi.framework.ServiceReference 参数。