12.3. 导入服务
概述
这部分描述了如何获取和使用已导出到 OSGi 服务 registry 的服务的引用。您可以使用参考元素或
元素来导入 OSGi 服务。reference
-listreference
元素适合访问 无状态 服务,而 reference-list
元素则适合访问 有状态 服务。
管理服务引用
支持以下用于获取 OSGi 服务引用的模型:
参考资料管理器
参考管理器 实例由 Blueprint 参考
元素创建。此元素返回单个服务引用,是访问 无状态 服务的首选方法。图 12.1 “对无状态服务的引用” 显示了使用参考管理器访问无状态服务的模型概述。
图 12.1. 对无状态服务的引用
客户端蓝图容器中的 Bean 使用代理对象( 提供的对象)注入,后者由 OSGi 服务 registry 中的 服务对象(后备服务)提供支持。此模型以以下方式明确利用无状态服务可交换的事实:
-
如果发现多个服务实例与
引用
元素中的条件匹配,则参考管理器可以任意选择其中一个作为后备实例(因为它们是相互交换的)。 - 如果后备服务消失,则参考管理器可以立即切换为使用同一类型的其它可用服务之一。因此,不能保证,从一种方法调用下一个方法,代理仍连接到同一后端服务。
因此,客户端和后备服务之间的合同 是无状态的,客户端 不得 认为它始终与同一个服务实例通信。如果没有匹配的服务实例可用,代理会在抛出 ServiceUnavailable
异常前等待一定时间。通过在 reference
元素上设置 timeout
属性,可以配置超时的长度。
参考列表管理器
参考资料 列表管理器 实例由 Blueprint reference-list
元素创建。此元素返回服务引用列表,是访问 有状态 服务的首选方法。图 12.2 “Stateful Services 的引用列表” 显示了使用参考列表管理器访问有状态服务的模型概述。
图 12.2. Stateful Services 的引用列表
客户端蓝图容器中的 Bean 使用 java.util.List
对象( 提供的对象)注入,其中包含代理对象列表。每个代理都由 OSGi 服务注册表中的唯一服务实例提供支持。与无状态模型不同,后备服务 不被视为 在此处相互交换。实际上,列表中的每个代理的生命周期都与相应后备服务的生命周期紧密链接:当服务在 OSGi 注册表中注册时,对应的代理会被同步创建并添加到代理列表中;当服务从 OSGi 注册表中取消注册时,对应的代理将从代理列表中同步删除。
因此,代理及其后备服务之间的合同是 有状态的,客户端可能会在特定代理上调用方法时假设,它始终 与同一 后备服务通信。但是,可能会发生后备服务不可用,在这种情况下,代理会变得过时。在过时的代理上调用方法的任何尝试都会生成 ServiceUnavailable
异常。
按接口匹配(无状态)
获取 状态 服务引用的最简单方法是通过使用 引用
元素上的 interface
属性来指定要匹配的接口。如果接口属性值是服务的超级类型,或者属性值是服务实施的 Java 接口( interface
属性可以指定 Java 类或 Java 接口
),则服务被认为是匹配的。
例如,要引用无状态 SavingsAccount
服务(请参阅 例 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>
其中 reference
元素创建 ID 为 savingsRef
的参考管理器 bean。要使用引用的服务,请将 savingsRef
bean 注入您的客户端类之一,如下所示。
注入客户端类的 bean 属性可以是从 SavingsAccount
分配的任何类型。例如,您可以定义 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 接口)。
interface
例如,要引用有状态的 SavingsAccount
服务(请参阅 例 12.1 “使用单一接口导出服务示例”),请定义 reference-list
元素,如下所示:
<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
的参考列表 manager bean。要使用引用的服务列表,请将 savingsListRef
bean 引用注入其中一个客户端类,如下所示。
默认情况下,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),请在 reference
元素中指定 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 元素上的 filter
属性或
元素来指定。reference
-listfilter
属性的值必须是 LDAP 过滤器表达式。例如,要定义在 bank.name
服务属性等于 HighStreetBank
时匹配的过滤器,您可以使用以下 LDAP 过滤器表达式:
(bank.name=HighStreetBank)
要匹配两个服务属性值,您可以使用 &
amp; 组合将表达式与逻辑 和
.For 例如,要求 foo
属性等于 FooValue
,bar
属性等于 BarValue
,您可以使用以下 LDAP 过滤器表达式:
(&(foo=FooValue)(bar=BarValue))
有关 LDAP 过滤器表达式的完整语法,请参阅 OSGi 内核规格的 3.2.7 部分。
过滤器也可以与 interface
和 component-name
设置结合使用,在这种情况下,所有指定条件都需要匹配。
例如,要匹配 SavingsAccount
类型的 无状态 服务,其 bank.name
服务属性等于 HighStreetBank
,您可以定义 参考
元素,如下所示:
<reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" filter="(bank.name=HighStreetBank)"/>
要匹配 SavingsAccount
类型 的有状态 服务,其 bank.name
服务属性等于 HighStreetBank
,您可以定义一个 reference-list
元素,如下所示:
<reference-list id="savingsRef" interface="org.fusesource.example.SavingsAccount" filter="(bank.name=HighStreetBank)"/>
指定是否强制或可选
默认情况下,假定对 OSGi 服务的引用是强制的(请参阅 必需的依赖项)。通过在元素上设置 availability
属性来自定义 reference
元素或 reference-list
元素的依赖项行为。
availability
属性有两个可能的值:
-
必需
(默认),这意味着在正常的 Blueprint 容器初始化过程中 必须 解决依赖项 -
可选
,表示在初始化过程中 不需要 解析依赖项。
以下 reference
元素的示例演示了如何明确声明引用是强制依赖项:
<reference id="savingsRef" interface="org.fusesource.example.SavingsAccount" availability="mandatory"/>
指定参考监听程序
为了应对 OSGi 环境的动态性质,例如,如果您声明了某些服务引用具有 可选
可用性,则通常在后备服务绑定到 registry 时以及从 registry 中绑定绑定时,这通常很有用。要接收服务绑定和 unbinding 事件的通知,您可以定义一个 reference-listener
元素作为 reference
元素或 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
类型的实例注册为侦听 bind
和 unbind
事件的回调。每当 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); } }
方法名称 onBind
和 onUnbind
分别由 bind-method
和 unbind-method
属性指定。这两个回调方法都采用 org.osgi.framework.ServiceReference
参数。