【BluePrint】使用 Blueprint Container 规范构建 OSGi 应用程序

写道
简介: Open Service Gateway initiative (OSGi) Alliance 框架变得越来越受欢迎。它为开发模块化的动态应用程序提供了良好的机制。最新发布的 OSGi Service Platform Release 4 V4.2 规范引入了 Blueprint Container 规范。在本文中,了解在不复杂化 Java™ 代码的情况下,Blueprint Container 如何为在 OSGi 环境中创建动态应用程序提供简单的编程模型。本文提供了大量示例帮助您开始使用 Blueprint XML 文件和组件 XML 定义。

OSGi 框架为开发模块化的动态应用程序提供了良好的机制,它最近正变得十分流行。最新的 OSGi Service Platform Release 4 V4.2 规范包括了一个名为 Blueprint Container 的规范。

Blueprint Container 规范为 OSGi 定义了一个 依赖性注入(dependency injection) 框架。它的目的是处理 OSGi 的动态特性,即服务可以在任何时间变得可用和不可用。该规范的另一个意图是处理普通旧 Java 对象(POJO),这样相同的对象就可以用于 OSGi 框架的内部和外部。定义并描述应用程序各个组件的 Blueprint XML 文件对 Blueprint 编程模型十分重要。规范描述了组件如何被实例化,以及如何相互连接在一起形成一个可以运行的应用程序。  

Spring 动态模块 写道
如果熟悉 Spring 框架的话,您将注意到 Spring 和 Blueprint Container 规范之间有许多相似点。Blueprint 规范的基础恰好是 Spring Dynamic Modules 项目。
.

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 XML 文件
  • 实例化
  • 将组件连接在一起

在初始化期间,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 属性。管理器 ID 在 Blueprint Container 内对于所有顶级管理器必须是唯一的。管理器使用 ID 彼此引用。例如,在注入期间,管理器将要求被引用的管理器提供对象,该对象将被注入到管理器正在创建的组件中。
activation
这个可选属性为管理器定义激活模式。可支持两种激活模式:
  • eager ,其中管理器在 Blueprint Container 初始化期间激活。
  • lazy ,其中管理器按需要激活。
默认情况下,启用 eager 激活模式。然而,通过对 blueprint 元素设置 default-activation 属性,可以为 Blueprint XML 文件内的所有管理器修改默认激活模式。当要求管理器提供其第一个组件实例时,管理器将被激活。当 Blueprint Container 被销毁时,管理器将被解除激活。每个管理器都拥有自己的激活和解除激活步骤。
dependsOn
这个可选属性指定了一个管理器 ID 列表。所列出的管理器将在其他管理器之前激活。管理器可以具有显式或隐式的依赖关系。 dependsOn 属性定义了显式的依赖关系。隐式依赖关系在管理器定义中通过对其他管理器的引用定义。

bean 管理器 创建具有给定参数和属性的 Java 对象的实例。bean 管理器可以根据范围设置创建一个或多个对象实例。它还可以管理对象的生命周期,并且在所有属性被注入或对象被销毁时通知对象。

在 Blueprint XML 中,bean 元素将定义一个 bean 管理器。用于对象构造的参数由 argument 元素指定;注入的属性则由 property 子元素指定。

签名解析(disambiguation) 写道
Blueprint 规范定义了一个分多个步骤的算法,用于解析签名。此算法考虑所有可能的构造函数以及每个构造函数中的参数组合,从而找到最佳匹配。相同的算法被用于方法中。规范的 121.9.1 小节介绍了详细内容。

在对象构造期间,Blueprint Container 必须首先找到合适的构造函数或工厂方法,这个构造函数或工厂方法具有一组类似的参数,这些参数与 XML 中指定的参数匹配。默认情况下,Blueprint Container 使用 XML 中的 argument 元素的数量和顺序查找匹配的构造函数或方法。如果 argument 元素无法根据自身的排列顺序映射到参数,那么 Blueprint Container 将尝试重新对 argument 属性排序并查找最匹配的组合。

为了帮助 Blueprint Container 选择合适的构造函数、方法或参数组合,可以在 argument 元素中指定额外的属性,例如 indextype 。例如,type 指定了一个列名,用于根据精确的类型将 argument 元素匹配到参数。

property 元素指定要注入的属性的名称和值。属性名与 Java 类中的 setter 方法名对应。例如,如果属性名为 foo ,那么对应的 setter 方法为 setFoo(arg) 。属性名和对应的 setter 方法名遵循 JavaBeans 规范中定义的属性设计模式。

argumentproperty 元素的值可以通过 valueref 属性指定,或者可以被内联。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
bean 管理器创建了 bean 的单个实例,并在每次要求管理器提供对象时返回该实例。
prototype
bean 管理器在每次要求管理器提供对象时都将创建一个 bean 的新实例。

默认情况下,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 规范指定了两种回调方法:

写道
init-method
指定了一种方法,该方法在所有属性被注入时将得到调用。
destroy-method
指定了一种方法,该方法在 Blueprint Container 销毁对象实例时得到调用。

     destroy-method 回调在 prototype 范围中不受 bean 支持。由应用程序负责销毁这些实例。所有生命周期方法都必须是公共的,不包含任何参数,并且不返回值。

清单 6 展示了一个 Java 类的实例,它具有生命周期方法和一个 Blueprint XML bean 条目,后者指定了 init-methoddestroy-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”/> 

引用和引用列表管理器具有一些相同的属性。三个相同的属性用于服务选择:interfacecomponent-namefilter

可以使用 interface 属性指定一个接口类。这个接口类用于两个目的:用于服务选择和服务代理。interface 属性是可选的,但是如果设置了该属性,那么它必须指定一个接口类。对于服务选择,接口类被用于从服务注册表(使用该接口名注册)中选择服务。对于服务代理,服务引用管理器返回的代理必须实现由接口类定义的所有方法。如果接口属性未被指定,那么代理的行为就类似于实现一个不包含任何方法的接口。

还可以将 component-namefilter 属性用于服务选择。component-name 是一种将 osgi.blueprint.compname=<component-name> 表达式添加到选择过滤器的方便方法。类似地,filter 属性指定将被添加到选择过滤器的原始 OSGi 过滤器表达式。interfacecomponent-namefilter 属性被结合在一起,创建一个用于服务选择的主要的 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-methodunbind-method 属性指定回调方法。提供回调方法的对象可以内联到 reference-listener 元素内,或指定为对顶级管理器的引用。

绑定回调或非绑定回调都可以拥有以下任意签名(anyMethod 表示一个任意的方法名)。

void anyMethod(ServiceReference)
该参数为被绑定或解除绑定的服务的 ServiceReference 对象。
void anyMethod(? super T)
该参数是被绑定或解除绑定的服务对象代理。服务对象必须能够指定类型 T。
void anyMethod(? super T, Map)
第一个参数为被绑定或解除绑定的服务对象代理。该服务对象必须能够指定类型 T。第二个参数提供与服务关联的服务属性。

如果引用侦听器对一个回调具有多个超载方法,那么将调用具有匹配签名的所有方法。

对于引用列表管理器,每当一个匹配服务被添加到服务注册表或从其中删除时,侦听器回调将得到调用。然而,对于引用管理器,当管理器已经被绑定到服务并且具有更低级别的匹配服务被添加到服务注册表时,绑定回调将不会被调用。类似地,当绑定到管理器的服务消失并且可以立即被另一个匹配服务取代,非绑定回调将不会被调用。

必须注意,当使用引用管理器并与有状态服务交互时,应当使用引用侦听器来跟踪代理的后端服务,从而恰当地管理服务的状态。

清单 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
结合 interfacesclass-hierarchy 选项。

清单 13 展示了使用三种方式指定服务接口的服务。在本例中,serviceOneserviceTwo 服务使用一个 Serializable 接口类注册,而 serviceThree 服务使用 MyAccountAccountSerializable 类注册。

  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-methodunregistration-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。

listsetarray 元素都属于集合,并且分别表示 java.util.List 对象、java.util.Set 对象和 Object[] 数组。这些集合的子元素可以是本节中描述的任意 XML 值元素。可以在这些元素上设置可选的 value-type 属性,用来为这些集合子元素指定默认类型。

清单 19 中的示例显示了如何将 XML 值元素结合在一起创建一个列表。创建的列表将包含以下四项:

  • 一个 “123” String
  • 一个 java.math.BigInteger 对象,其值为 456
  • 一个 null 值
  • 一个 java.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-refvalue-ref 属性指定顶级管理器的 ID,并用于从指定管理器以属性键或值的形式获得对象。map 元素可以指定 key-typevalue-type 属性,从而为键和值定义一个默认类型。

清单 21 展示了 map 值元素的一个例子。它还展示了如何以各种方式构建 map 对象的条目。创建的 map 对象将包含以下条目:

  • 一个 myKey1 String 键,被映射到 myValue String
  • 一个键,是由 account bean 管理器返回的对象,该键被映射到 myValue String
  • 一个键,是一个 java.lang.Integer 对象,其值为 123,该键被映射到 myValue String
  • 一个 myKey2 String 键,该键被映射到的值是一个由 account bean 管理器返回的对象。
  • 一个 myKey3 String 键,该键被映射到的值是一个值为 345 的 java.lang.Long 对象。
  • 一个键,是一个值为 urn:ibm 的 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 提供了许多内置转换,比如:

  • 将字符串值转换为所有原语类型、包装器类型或任何具有公共构造函数(具有一个 String 值)的类型。
  • array 元素转换为集合对象(具有类似的成员类型)。
  • listset 元素转换为数组对象(具有类似的成员类型)。

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

关于下载方法的信息

学习

  • 查看 JavaWorld 的 OSGi Bundles for beginners 教程。了解如何在一个 OSGi 容器中创建、执行和管理包。
  • 了解更多有关 依赖性注入 的内容。
    了解更多有关 OSGi Alliance 的内容,并获得最新的新闻、事件、论坛和规范等。
    Apache Geronimo 提供了一个服务器运行时框架,它结合使用各种开源技术,创建了可以满足开发人员和系统管理员需求的运行时。
    了解有关 Apache Felix 的全部内容。
  • 获得 Apache Felix Karaf 的概览。
  • 属性名和对应的 setter 方法名必须遵循 JavaBeans 规范中定义的属性设计模式。
  • 了解 Spring Dynamic Modules 如何简化运行在 OSGi 框架中的 Spring 应用程序的构建。
  • 要收听针对软件开发人员的有趣访谈和讨论,请查看 developerWorks podcasts
  • 随时关注 developerWorks 技术活动网络广播
  • 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他 活动
  • 访问 developerWorks 开源专区 ,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用,并访问我们的 最受欢迎的文章和教程
  • 查看免费的 developerWorks 演示中心 ,观看并了解 IBM 及开源技术和产品功能。

获得产品和技术

猜你喜欢

转载自marsvaadin.iteye.com/blog/1391864