OSGi 框架为开发模块化的动态应用程序提供了良好的机制,它最近正变得十分流行。最新的 OSGi Service Platform Release 4 V4.2 规范包括了一个名为 Blueprint Container 的规范。
Blueprint Container 规范为 OSGi 定义了一个 依赖性注入(dependency injection) 框架。它的目的是处理 OSGi 的动态特性,即服务可以在任何时间变得可用和不可用。该规范的另一个意图是处理普通旧 Java 对象(POJO),这样相同的对象就可以用于 OSGi 框架的内部和外部。定义并描述应用程序各个组件的 Blueprint XML 文件对 Blueprint 编程模型十分重要。规范描述了组件如何被实例化,以及如何相互连接在一起形成一个可以运行的应用程序。
Blueprint Container 规范为 OSGi 定义了一个 依赖性注入(dependency injection) 框架。它的目的是处理 OSGi 的动态特性,即服务可以在任何时间变得可用和不可用。该规范的另一个意图是处理普通旧 Java 对象(POJO),这样相同的对象就可以用于 OSGi 框架的内部和外部。定义并描述应用程序各个组件的 Blueprint XML 文件对 Blueprint 编程模型十分重要。规范描述了组件如何被实例化,以及如何相互连接在一起形成一个可以运行的应用程序。
Blueprint Container 规范使用了一个扩展器(extender)模式,借助这种模式,扩展器包监视框架中的包的状态,并根据这些包的状态为它们执行操作。Blueprint 扩展器包等待包被激活,然后检查它们是否是 Blueprint 包。如果一个包包含一个或多个 Blueprint XML 文件,那么它就被认为是 Blueprint 包。这些 XML 文件位于一个 OSGI-INF/blueprint/ 目录下的某个固定位置,或者在 Bundle-Blueprint
清单头部中显式指定。
一旦扩展器确定某个包是 Blueprint 包后,它将为这个包创建一个 Blueprint Container。这个 Blueprint Container 负责完成以下操作:
在初始化期间,Blueprint Container 确保强制服务引用得到满足,将所有服务注册到服务注册表中,然后创建初始的组件实例。Blueprint 扩展器包还在包停止后为该包销毁 Blueprint Container。
本文将关注 Blueprint XML。通过若干示例展示组件 XML 定义及其使用。
Blueprint XML 文件被标识为顶级 blueprint
元素,如清单 1 所示。
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns=”http://www.osgi.org/xmlns/blueprint/v1.0.0”> ... </blueprint>
Blueprint XML 文件包含各种组件管理器的定义。Blueprint Container 规范定义了四种主要的组件管理器:一个 bean manager 、一个 service manager 和两个 service reference managers 。每种管理器都负责创建和管理所创建组件的生命周期。管理器提供了一个组件实例。每个管理器都拥有相应的 XML 元素,用于描述管理器属性。管理器可以是顶级管理器,或者内联在其他管理器定义内。管理器还具有一些通用的属性。
id
id
属性是可选属性。如果没有指定的话,将自动生成一个唯一 ID 并分配给顶级管理器。内联管理器被认为是匿名的,因此不允许设置
id
属性。管理器 ID 在 Blueprint Container 内对于所有顶级管理器必须是唯一的。管理器使用 ID 彼此引用。例如,在注入期间,管理器将要求被引用的管理器提供对象,该对象将被注入到管理器正在创建的组件中。
activation
eager
,其中管理器在 Blueprint Container 初始化期间激活。lazy
,其中管理器按需要激活。eager
激活模式。然而,通过对
blueprint
元素设置
default-activation
属性,可以为 Blueprint XML 文件内的所有管理器修改默认激活模式。当要求管理器提供其第一个组件实例时,管理器将被激活。当 Blueprint Container 被销毁时,管理器将被解除激活。每个管理器都拥有自己的激活和解除激活步骤。
dependsOn
dependsOn
属性定义了显式的依赖关系。隐式依赖关系在管理器定义中通过对其他管理器的引用定义。
bean 管理器 创建具有给定参数和属性的 Java 对象的实例。bean 管理器可以根据范围设置创建一个或多个对象实例。它还可以管理对象的生命周期,并且在所有属性被注入或对象被销毁时通知对象。
在 Blueprint XML 中,bean
元素将定义一个 bean 管理器。用于对象构造的参数由 argument
元素指定;注入的属性则由 property
子元素指定。
在对象构造期间,Blueprint Container 必须首先找到合适的构造函数或工厂方法,这个构造函数或工厂方法具有一组类似的参数,这些参数与 XML 中指定的参数匹配。默认情况下,Blueprint Container 使用 XML 中的 argument
元素的数量和顺序查找匹配的构造函数或方法。如果 argument
元素无法根据自身的排列顺序映射到参数,那么 Blueprint Container 将尝试重新对 argument
属性排序并查找最匹配的组合。
为了帮助 Blueprint Container 选择合适的构造函数、方法或参数组合,可以在 argument
元素中指定额外的属性,例如 index
或 type
。例如,type
指定了一个列名,用于根据精确的类型将 argument
元素匹配到参数。
property
元素指定要注入的属性的名称和值。属性名与 Java 类中的 setter 方法名对应。例如,如果属性名为 foo
,那么对应的 setter 方法为 setFoo(arg)
。属性名和对应的 setter 方法名遵循 JavaBeans 规范中定义的属性设计模式。
argument
和 property
元素的值可以通过 value
或 ref
属性指定,或者可以被内联。ref
属性指定顶级管理器的 ID,并用于从引用管理器获得对象,作为参数或属性值。内联的值可以为 “Object values” 小节中描述的任意 XML 值。
bean 可以通过以下内容以三种方式构建:
清单 2、3 和 4 演示了三种构建 Java 对象的方法。每一个清单展示一个不完整的 Java 类以及用于驱动对象创建的相应的 Blueprint XML 片段。
清单 2 展示了一个简单的构造器创建示例。在这个例子中,class
属性指定将要实例化的 Java 类的名称。Blueprint Container 将创建 Account
对象,方式为将 1
作为参数传递给构造器并调用 setDescription()
方法注入描述属性。
public class Account { public Account(long number) { ... } public void setDescription(String description) { ... } public String getDescription() { ... } } <bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”> <argument value=”1”/> <property name=”description” value=”#1 account”/> </bean>
清单 3 展示了一个静态的工厂方法构造。在这个例子中,class
属性指定了包含静态工厂方法的类的名称。这个静态工厂方法的名称由 factory-method
属性指定。Blueprint Container 将对 AccountFactory
类调用 createAccount()
静态方法并将 2
作为参数传递,用于创建 Account
对象。当工厂返回创建的对象后,容器将向它注入描述属性。
public class StaticAccountFactory { public static Account createAccount(long number) { return new Account(number); } } <bean id=”accountTwo” class=“org.apache.geronimo.osgi.StaticAccountFactory” factory-method=“createAccount”> <argument value=”2”/> <property name=”description” value=”#2 account”/> </bean>
对于实例工厂方法构建,如清单 4 所示,使用了两个管理器。其中一个管理器是工厂,另一个管理器使用该工厂创建对象。factory-ref
用来指定顶级 bean 的 ID 或行为类似工厂的引用管理器。提供的工厂对象必须具有一个由 factory-method
属性指定的工厂方法。
在本例中,accountFactory
bean 管理器是一个工厂。Blueprint Container 将首先创建 AccountFactory
实例,该实例具有自己的参数和属性。在本例中,只指定了一个参数:工厂名。Blueprint Container 随后将对 AccountFactory
实例调用 createAccount()
方法,然后将 3
作为参数传递,以创建 Account
对象。一旦工厂返回创建的对象,容器将向其注入描述属性。
public class AccountFactory { public AccountFactory(String factoryName) { ... } public Account createAccount(long number) { return new Account(number); } } <bean id=”accountFactory” class=“org.apache.geronimo.osgi.AccountFactory”> <argument value=”account factory”/> </bean> <bean id=”accountThree” factory-ref=“accountFactory” factory-method=“createAccount”> <argument value=”3”/> <property name=”description” value=”#3 account”/> </bean>
根据范围设置,一个 bean 管理器可以创建一个或多个对象实例。Blueprint Container 规范指定了两个主要的范围:
singleton
prototype
默认情况下,singleton
范围被应用于顶级 bean 管理器。scope
属性不能在内联 bean 管理器中设置,因此内联管理器总是被认为具有 prototype
范围。
scope
属性用于指定范围设置。清单 5 展示了两个具有不同范围设置的 bean 定义。
<bean id=”prototypeAccount” class=“org.apache.geronimo.osgi.Account” scope=”prototype”> <argument value=”4”/> </bean> <bean id=”singletonAccount” class=“org.apache.geronimo.osgi.Account” scope=”singleton”> <argument value=”5”/> </bean>
bean 管理器还可以管理它所创建的对象的生命周期,并在所有属性被注入或对象被销毁时通知对象。Blueprint Container 规范指定了两种回调方法:
destroy-method
回调在 prototype
范围中不受 bean 支持。由应用程序负责销毁这些实例。所有生命周期方法都必须是公共的,不包含任何参数,并且不返回值。
清单 6 展示了一个 Java 类的实例,它具有生命周期方法和一个 Blueprint XML bean
条目,后者指定了 init-method
和 destroy-method
属性。
public class Account { public Account(long number) { ... } public void init() { ... } public void destroy() { ... } } <bean id=”accountFour” class=“org.apache.geronimo.osgi.Account” init-method=”init” destroy-method=”destroy”> <argument value=”6”/> <property name=”description” value=”#6 account”/> </bean>
服务引用管理器提供了对 OSGi 服务注册表中已注册服务的访问。Blueprint 规范定义了两种服务引用管理器:引用(reference)和引用列表(reference list)管理器。
引用管理器提供了一个对象,该对象充当在服务注册表中注册了的实际服务的代理。代理允许被注入的对象保持不变,同时后端服务可以变化或被其他服务取代。对不具备后端服务的代理的调用将被阻塞,直到服务变得可用或发生超时。
引用管理器由 reference
元素定义。代理用于等待后端服务变为可用的时间的长度将由 timeout
属性指定。如果 timeout
属性没有被指定,那么默认的超时值为 5 分钟。也可以为 Blueprint XML 文件中的所有引用管理器修改默认超时,只需要修改 blueprint
元素的 default-timeout
属性。所有超时值都以毫秒为单位指定;值为 0 则表示无限期超时。
清单 7 展示了一个简单的引用管理器示例。服务代理将拥有一个 30 秒的超时。
<reference id=”serviceReferenceOne” interface=”java.io.Serializable” timeout=”30000”/>
列表管理器提供了一个 List
对象,其中包含服务代理对象或 ServiceReference
对象,具体取决于成员类型设置。提供的 List
对象是动态的,因为随着匹配服务被添加到服务注册表或从中移除,该对象可以增加或缩小。List
对象是只读的并且只支持一部分 List
API。
引用列表管理器中的代理不同于引用管理器中的代理。它们使用固定的后端服务,不存在超时,并且如果后端服务变得不可用,将立即抛出 ServiceUnavailableException
。
引用列表管理器由 reference-list
元素定义。提供的 List
对象的成员类型由 member-type
属性指定。member-type
属性支持两种值:
service-object
service-reference
ServiceReference
对象列表。
清单 8 展示了一个简单的引用列表管理器示例。List
成员为服务代理。
<reference-list id=”serviceReferenceListOne” interface=”java.io.Serializable” member-type=”service-object”/>
引用和引用列表管理器具有一些相同的属性。三个相同的属性用于服务选择:interface
、component-name
和 filter
。
可以使用 interface
属性指定一个接口类。这个接口类用于两个目的:用于服务选择和服务代理。interface
属性是可选的,但是如果设置了该属性,那么它必须指定一个接口类。对于服务选择,接口类被用于从服务注册表(使用该接口名注册)中选择服务。对于服务代理,服务引用管理器返回的代理必须实现由接口类定义的所有方法。如果接口属性未被指定,那么代理的行为就类似于实现一个不包含任何方法的接口。
还可以将 component-name
和 filter
属性用于服务选择。component-name
是一种将 osgi.blueprint.compname=<component-name>
表达式添加到选择过滤器的方便方法。类似地,filter
属性指定将被添加到选择过滤器的原始 OSGi 过滤器表达式。interface
、component-name
和 filter
属性被结合在一起,创建一个用于服务选择的主要的 OSGi 过滤器表达式。
例如,清单 7 中的引用的选择过滤器为 (&(objectClass=java.io.Serializable))
,而清单 9 中的引用的选择过滤器为 (&(objectClass=java.io.Serializable)(osgi.blueprint.compname=myAccount)(mode=shared))
。
<reference id=”serviceReferenceTwo” interface=”java.io.Serializable” component-name=”myAccount” filter=”(mode=shared)”/>
Blueprint Container 继续进行初始化之前,服务引用管理器需要至少一个服务匹配其选择条件。这一要求是由 availability
属性控制的。availability
属性可以有两个值:
optional
mandatory
默认情况下,假定使用 mandatory
可用性。通过使用 blueprint
元素的 default-availability
属性,可以为 Blueprint XML 中的所有服务引用管理器修改默认可用性设置。
具有 mandatory
可用性的服务引用管理器(具有一个匹配服务)被认为是可以满足需求的。具有 optional
可用性的服务引用管理器总是被认为可以满足需求,即使它不具备任何匹配的服务。Blueprint Container 初始化将被延迟,除非所有强制服务引用管理器都得到满足。
必须要注意的一点是,只有在 Blueprint Container 初始化期间才考虑 mandatory
可用性。完成初始化后,随着服务在任意时刻的变化,强制服务引用将不能被满足。
清单 10 展示了一个具有 mandatory
可用性的引用管理器的示例。
<reference id=”serviceReferenceThree” interface=”java.io.Serializable” timeout=”30000” availability=”mandatory”/>
所有服务引用管理器都可以具有 0 个或多个引用侦听器。引用侦听器 指这样一些对象:当服务引用管理器选择了服务或当服务引用管理器不再使用服务时,调用回调方法。引用侦听器使用 reference-listener
元素指定。bind-method
和 unbind-method
属性指定回调方法。提供回调方法的对象可以内联到 reference-listener
元素内,或指定为对顶级管理器的引用。
绑定回调或非绑定回调都可以拥有以下任意签名(anyMethod
表示一个任意的方法名)。
void anyMethod(ServiceReference)
ServiceReference
对象。
void anyMethod(? super T)
void anyMethod(? super T, Map)
如果引用侦听器对一个回调具有多个超载方法,那么将调用具有匹配签名的所有方法。
对于引用列表管理器,每当一个匹配服务被添加到服务注册表或从其中删除时,侦听器回调将得到调用。然而,对于引用管理器,当管理器已经被绑定到服务并且具有更低级别的匹配服务被添加到服务注册表时,绑定回调将不会被调用。类似地,当绑定到管理器的服务消失并且可以立即被另一个匹配服务取代,非绑定回调将不会被调用。
必须注意,当使用引用管理器并与有状态服务交互时,应当使用引用侦听器来跟踪代理的后端服务,从而恰当地管理服务的状态。
清单 11 展示了一个简单的注册侦听器示例。ReferenceListener
类有两个绑定回调方法和一个非绑定回调方法,它们将作为来自服务引用列表管理器的绑定或非绑定服务被调用。
public class ReferenceListener { public void bind(ServiceReference reference) { ... } public void bind(Serializable service) { ... } public void unbind(ServiceReference reference) { ... } } <reference-list id=”serviceReferenceListTwo” interface=”java.io.Serializable” availability=”optional”> <reference-listener bind-method=”bind” unbind-method=”unbind”> <bean class=“org.apache.geronimo.osgi.ReferenceListener”/> </reference-listener> </reference-list>
服务管理器在 OSGi 服务注册表中注册服务。如果某个服务对所有强制服务引用管理器的依赖关系得到满足,服务管理器将注册或解除注册这个服务。如果强制服务引用在某一刻不能被满足,那么服务管理器将返回一个 ServiceRegistration
代理对象,后者将调用委托给实际的 ServiceRegistration
对象。
在初始化期间,Blueprint Container 需要为其中的每个服务管理器注册基于 ServiceFactory
的服务,而不是实际的服务对象。这些服务允许 Blueprint Container 延迟对象插件,因为它们将拦截服务请求并只在需要时实例化实际的服务对象。实际的服务对象由另一个管理器提供,通常为一个 bean 管理器。实际的服务对象可以实现 OSGi ServiceFactory
接口。
在 Blueprint XML 中,service
元素定义一个服务管理器。提供服务对象的管理器可以通过 ref
属性引用,或内联到 service
元素内。清单 12 展示了两个服务:一个具有引用管理器,另一个具有内联管理器。
<service id=”serviceOne” ref=”account” ... /> <service id=”serviceTwo” … > <bean class=“org.apache.geronimo.osgi.Account”> <argument value=”123”/> </bean> </service>
每个服务都必须注册到一个或多个接口名下。服务的接口名列表可以显式指定,或由服务对象根据自动导出设置自动确定。接口名可以使用以下任一方法显式地设置:
interface
属性,只能指定一个单一的接口名。 interfaces
子元素,允许设置任意数量的接口。 自动导出设置由 auto-export
属性指定,并且支持以下 4 种选项。
disabled
auto-export
属性未被指定,则该选项为默认值。接口列表必须使用
interface
属性或
interfaces
子元素指定。
interfaces
class-hierarchy
all-classes
interfaces
和
class-hierarchy
选项。
清单 13 展示了使用三种方式指定服务接口的服务。在本例中,serviceOne
和 serviceTwo
服务使用一个 Serializable
接口类注册,而 serviceThree
服务使用 MyAccount
、Account
和 Serializable
类注册。
public class MyAccount extends Account implements java.io.Serializable { ... } <bean id=”myAccount” class=”org.apache.geronimo.osgi.MyAccount”> <argument value=”7”/> <property name=”description” value=”MyAccount”/> </bean> <service id=”serviceOne” ref=”myAccount” interface=”java.io.Serializable”/> <service id=”serviceTwo” ref=”myAccount”> <interfaces> <value>java.io.Serializable</value> </interfaces> </service> <service id=”serviceThree” ref=”myAccount” autoExport=”all-classes”/>
服务也可以由一组属性注册,这些属性可以使用 service-properties
子元素指定。service-properties
元素包含多个 entry
子元素,每个子元素代表不同的属性。属性键使用 key
属性指定,但是属性值可以被指定为 value
属性或内联到元素内。服务属性值可以是不同类型,但是只支持原语、原语包装器类、集合或原语类型数组。
清单 14 展示了使用两个服务属性注册的服务的示例。active
服务属性有一个 java.lang.Boolean
类型的值。mode
属性的类型为 String
。
<service id=”serviceFour” ref=”myAccount” autoExport=”all-classes”> <service-properties> <entry key=”mode” value=”shared”/> <entry key=”active”> <value type=”java.lang.Boolean”>true</value> </entry> </service-properties> </service>
如果为服务提供服务对象的管理器是顶级管理器,osgi.blueprint.compname
属性将被自动添加到服务属性中。osgi.blueprint.compname
属性的值为提供服务实例的顶级管理器的 ID。例如,对于清单 14 中的服务例子,osgi.blueprint.compname
属性的值将被设置为 myAccount
。
可以使用 ranking
属性公开具有特定级别的服务。如果 ranking 属性未被指定,那么服务在注册时就不会具有级别。在这种情况下,OSGi 框架假设服务的默认级别为 0
。
下面给出了一个具有级别的服务注册示例。
<service id=”serviceFive” ref=”myAccount” auto-export=”all-classes” ranking=”3”/>
服务管理器可以具有 0 个或多个注册侦听器 ,注册侦听器就是指在服务被注册之后或服务被解除注册之前立即调用回调方法的对象。注册侦听器使用 registration-listener
子元素指定。registration-method
和 unregistration-method
属性指定回调方法。提供回调方法的对象可以内联到 registration-listener
元素中,或者被指定为对顶级管理器的引用。
注册回调方法或非注册回调方法的签名取决于服务对象是否实现 ServiceFactory
接口。如果服务实现 ServiceFactory
接口,那么两者都必须具有一个 void anyMethod(ServiceFactory, Map)
签名,其中 anyMethod
表示一个任意的方法名。
如果服务没有实现 ServiceFactory
接口,那么所有回调方法都必须匹配 void anyMethod(? super T, Map)
签名,其中服务对象的类型可以被指定为 T 类型。回调方法的第一个参数是服务对象的实例,第二个参数是注册属性。如果注册侦听器对一个回调具有多个超载方法,那么具有匹配签名的所有方法都将被调用。
清单 16 展示了一个简单的注册侦听器示例。
public class RegistrationListener { public void register(Account account, Map properties) { ... } public void unregister(Account account, Map properties) { ... } } <service id=”serviceSix” ref=”myAccount” auto-export=”all-classes”> <registration-listener registration-method=”register” unregistration-method=”unregister”> <bean class=“org.apache.geronimo.osgi.RegistrationListener”/> </registration-listener> </service>
Blueprint Container 规范还定义了许多特殊的环境管理器,它们设置 ID 并提供对环境组件的访问。它们不具有 XML 定义,并且也不能被重写,因为它们的 ID 被保护起来,不能被其他管理器使用。环境管理器提供的对象只能被注入到使用引用的其他管理器中。Blueprint Container 规范定义了 4 种环境管理器:
blueprintBundle
Bundle
对象。
blueprintBundleContext
BundleContext
对象。
blueprintContainer
BlueprintContainer
对象。
blueprintConverter
Converter
对象,提供了对 Blueprint Container 类型转换工具的访问。
类型转换 小节详细对此进行介绍。
清单 17 展示了一个简单示例,其中由 blueprintBundle
环境管理器提供的 Bundle
对象被注入到 accountManagerOne
bean 中。
public class AccountManager { public void setManagerBundle(Bundle bundle) { ... } } <bean id=”accountManagerOne” class=”org.apache.geronimo.osgi.AccountManager”> <property name=”managerBundle” ref=”blueprintBundle”/> </bean>
Blueprint Container 规范定义了许多 XML 元素,它们描述各种类型的对象值。这些 XML 值元素被用于管理器定义内。例如,它们可以用于 bean 管理器,以指定参数或属性值,或用于在服务管理器内指定服务属性的值。XML 值元素被转换为实际的值对象,并被注入到管理器组件中。
ref
元素定义了一个对顶级管理器的引用。component-id
属性指定了顶级管理器的 ID。注入的值将为由引用管理器返回的对象。
清单 18 展示了 ref
值元素的一个例子。accountOne
bean 实例被注入到 accountManagerTwo
bean 的 managedAccount
属性。
public class AccountManager { ... public void setManagedAccount(Account account) { ... } } <bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”> <argument value=”1”/> <property name="description" value="#1 account"/> </bean> <bean id=”accountManagerTwo” class=“org.apache.geronimo.osgi.AccountManager”> <property name=”managedAccount”> <ref component-id=”accountOne”/> </property> </bean>
idref
元素定义了顶级管理器的 ID。被注入的值为组件 id,由 component-id
属性指定。idref
元素被用于确保在具有指定 ID 的管理器被激活之前,该管理器确实存在。
value
元素表示将要从元素的字符串内容中创建的对象。您可以使用可选的 type
属性指定一种类型,字符串内容应该被转换为这种类型。如果 type
属性未被指定,字符串内容将被转换为在注入时使用的类型。
null
元素表示 Java null。
list
、set
和 array
元素都属于集合,并且分别表示 java.util.List
对象、java.util.Set
对象和 Object[]
数组。这些集合的子元素可以是本节中描述的任意 XML 值元素。可以在这些元素上设置可选的 value-type
属性,用来为这些集合子元素指定默认类型。
清单 19 中的示例显示了如何将 XML 值元素结合在一起创建一个列表。创建的列表将包含以下四项:
String
java.math.BigInteger
对象,其值为 456java.util.Set
对象,具有两个类型为 java.lang.Integer
的值<list> <value>123</value> <value type=”java.math.BigInteger”>456</value> <null/> <set value-type=”java.lang.Integer”> <value>1</value> <value>2</value> </set> </list>
props
元素表示 java.util.Properties
对象,其中键和值均为 String
类型。prop
子元素表示各种属性。属性键使用 key
属性指定,但是属性值则可以被指定为一个 value
属性或指定为元素的内容。
清单 20 给出了 props
值元素的一个例子。
<props> <prop key=”yes”>good</prop> <prop key=”no” value=”bad”/> </props>
map
元素表示一个 java.util.Map
对象,其中键和值可以为任意对象。entry
子元素表示各种属性。键可以被指定为一个 key
属性、一个 key-ref
属性,或内联到 key
子元素中。值可以被指定为 value
属性、value-ref
属性,或进行内联。内联的值可以被指定为本节描述的任意 XML 值元素。然而,内联键可以被指定为除 null
元素以外的任意 XML 值元素。Blueprint Specification 不允许在 map
元素中使用 Null 键。key-ref
和 value-ref
属性指定顶级管理器的 ID,并用于从指定管理器以属性键或值的形式获得对象。map
元素可以指定 key-type
和 value-type
属性,从而为键和值定义一个默认类型。
清单 21 展示了 map
值元素的一个例子。它还展示了如何以各种方式构建 map
对象的条目。创建的 map
对象将包含以下条目:
String
键,被映射到 myValue String
。 account
bean 管理器返回的对象,该键被映射到 myValue String
。java.lang.Integer
对象,其值为 123,该键被映射到 myValue String
。String
键,该键被映射到的值是一个由 account
bean 管理器返回的对象。String
键,该键被映射到的值是一个值为 345 的 java.lang.Long
对象。java.net.URI
对象,该键被映射到的值是一个 java.net.URL
对象,其值为 http://ibm.com。<map> <entry key=”myKey1” value=”myValue”/> <entry key-ref=”account” value=”myValue”/> <entry value=”myValue”> <key> <value type=”java.lang.Integer”>123</value> <key/> </entry> <entry key=”myKey2” value-ref=”account”> <entry key=”myKey3”> <value type=”java.lang.Long”>345</value> </entry> <entry> <key> <value type=”java.net.URI”>urn:ibm</value> <key/> <value type=”java.net.URL”>http://ibm.com</value> </entry> </map>
每个管理器都可以被内联为一个值。清单 22 展示了一个内联的 bean 管理器示例。
<bean id=”accountManagerThree” class=“org.apache.geronimo.osgi.AccountManager”> <property name=”managedAccount”> <bean class=“org.apache.geronimo.osgi.Account”> <argument value=”10”/> <property name="description" value="Inlined Account"/> </bean> </property> </bean>
在注入期间,Blueprint Container 将 XML 值元素转换为实际的对象。它将根据被注入属性的类型转换元素。例如,在 清单 2 中,Blueprint Container 将 123 String 值转换为一个 long
值。Blueprint Container 提供了许多内置转换,比如:
array
元素转换为集合对象(具有类似的成员类型)。list
或 set
元素转换为数组对象(具有类似的成员类型)。 Blueprint Container 还支持泛型(generic)。如果泛型信息可用,Blueprint Container 将使用该信息执行转换。例如,在清单 23 中,list
元素将被转换为一个 java.util.Long
对象列表。
public class AccountManager { ... public void setAccountNumbers(List<Long> accounts) { ... } } <bean id=”accountManagerFour” class=“org.apache.geronimo.osgi.AccountManager”> <property name=”accountNumbers”> <list> <value>123</value> <value>456</value> <value>789</value> </list> </property> </bean>
除了内置转换外,Blueprint 包可以提供其自己的转换。定制转换器是一些特定的 bean 管理器,它们提供了可以实现 Blueprint 的 Converter
接口的对象。定制转换器在 blueprint
元素下的 type-converters
元素内部指定。类型转换器在 Blueprint Container 初始化期间被首先初始化,因此其他管理器可以利用定制转换器。Blueprint Container 规范提供了有关编写定制转换器的更多信息。
Apache Felix Karaf 是 OSGi 应用程序的一个小容器。它提供了应用程序所需的一组基本组件和功能。例如,它提供了一个登录系统、热部署功能、动态配置、可扩展的 shell 控制台以及基本的管理功能。Karaf 还包含一个 Blueprint Container 实现。Blueprint 实现最初在 Apache Geronimo 项目中开发,并且完全符合 Blueprint Container 规范。
本节的例子将使用 Karaf 部署一个样例 Blueprint 包。样例包包含本文展示的众多 Java 类和 Blueprint XML 定义,为使用 Blueprint 提供了一个良好的开端。
要安装 Karaf,为您的平台 下载 二进制发行版文件,并将其提取到所选目录。
要启动 Karaf,使用以下命令。
cd <karaf_install_dir> bin/karaf
成功启动后将显示 Karaf shell 控制台。
下载本帖子附件里面附带的样例 Blueprint 包并将其解压缩到所选目录。使用 Apache Maven 构建样例包的源代码。运行清单 25 中的命令来构建包。
cd blueprint-sample mvn install
一旦成功构建样例后,在 blueprint-sample/target 目录中将创建一个 blueprint-sample-1.0.jar 包。现在,您可以将包部署到 Karaf。
要将包部署到 Karaf,最简单的一种方法就是将包 JAR 文件复制到 Karaf 的 deploy
目录。清单 26 展示了 copy
命令的一个例子。另一种部署包的方法是在 Karaf shell 中使用 osgi:install
命令。
cp target/blueprint-sample-1.0.jar <karaf_install_dir>/deploy
当成功部署包(并启动)后,应当会在 shell 中看到如图 1 所示的输出。
Blueprint Container 规范引入了最新的 OSGi 规范,为在 OSGi 环境中创建动态应用程序提供了一种简单、易用的编程模型,并且不会增加 Java 代码的复杂性。通过使用 Geronimo Blueprint Container 实现和 Apache Felix Karaf,您可以开始构建基于 Blueprint 的应用程序了。
样例 Blueprint 包源代码 | os-osgiblueprint-sample.zip | 7KB | HTTP |
学习
获得产品和技术