Gemini Blueprint参考文档 第7章 Bundle和Application上下文

来源:互联网 发布:c罗身体数据 编辑:程序博客网 时间:2024/06/07 00:41

7 BundleApplication上下文

OSGi中部署(模块)的单元是bundle(参见OSGi服务平台核心规范,即OSGi Service Platform Core Specification3.2)。对于OSGi运行时来说,一个bundle会处于三个稳定的状态之一:installedresolvedactiveBundle可能会导出服务对象到OSGi服务注册表,这样其它bundle就可以发现和使用这些服务。Bundle可能也会导出Java package,这样其它的bundle就可以导入这些类型。

Spring中,模块的主要单元是应用上下文(application context),这个上下文包含一些beans(Springapplication上下文管理)Application上下文以层次化的方式配置,这样应用上下文就可以看到父上下文定义的bean,但反之不成立。Spring输出方(exporter)和工厂bean的概念用于将bean的引用导出给应用上下文外面的客户端,并注入应用上下文外面定义服务引用。

OSGi bundleSpring应用上下文之间有着天然的密切关系。使用Gemini Blueprint,一个active bundle可能包含Spring的应用上下文,负责对象的实例化、配置、组装、装饰。这些bean有的可以导出为OSGi服务,这样其它的bundle就可以访问到。这个bundle内的bean也可以透明地被注入OSGi服务的引用。

本章介绍bundle和它们的应用上下文之间的生命周期关系。以及在OSGi环境中,基于在运行时产生的事件,这些关系如何受到Gemini Blueprint的影响。

7.1. Gemini Blueprint Extender Bundle

Extender扩展器模式

OSGi应用中的常见模式是扩展器extender,即(引述Peter Kriens, OSGi技术总监的话)在特定的领域,允许其它的bundle扩展功能。参见this OSGi联盟博客,了解更深入的解释。

Gemini Blueprint extender负责检测基于Springbundle,实例化它们的应用上下文。它就跟Spring Web应用中的ContextLoaderListener的用途是一样的。一旦,这个extender bundle安装并启动了,那么它就会查找所有的基于Spring并且已经处于Active状态的bundle,创建它们的应用上下文。另外,它会监听bundle的启动事件,自动创建后面启动的基于Springbundle的应用上下文。8.1“Bundle格式和Manifest介绍了extender如何识别"基于Springbundle",而8.3“Extender配置选项介绍extender如何配置。Extender监视它管理的bundle的生命周期,bundle停止时会自动销毁它的应用上下文。当extender bundle自己停止的时候,它会自动关闭它管理的所有应用上下文,基于它们之间的服务依赖。extender bundle symbolic nameorg.eclipse.gemini.blueprint.extender

7.2. 创建Application上下文

一旦extender启动,它就会分析已经启动的bundle和监视将要启动的bundle。如果一旦检测到Blueprint或者Gemini Blueprint的配置时,那么extender就会在一个不同的线程中以异步方式创建应用上下文,而不是在启动bundle的线程中(发送STARTED事件)。这个行为遵循了OSGi规范的建议,它确保了快速启动OSGi服务平台,以及在启动时确保服务的内部依赖不会发生死锁(互相等待),如下图所示:

Application Context Sequence Diagram

Extender只考虑那些成功启动的bunlde,即bundle处于ACTIVE状态;其它状态的bundle被忽略。因此基于Spring或者Blueprintbundle是在它完全启动之后,它的应用上下文才会被创建。对于所有启动的bunlde,它强制应用上下文按照一个bundle,一个bundle的同步的、串行的创建。参见第8.1节“Bundle格式和Manifest头”,了解这个行为的详细信息。

如果应用上下文因为某种原因失败,那么这个失败就会被记录到日志中。这个bundle仍然处于ACTIVE状态。应用上下文的生命周期无论如何也不会影响bundle的生命周期。由于应用上下文创建失败,所以与之有关的功能也没有了。例如没有来自应用上下文的服务导出到注册表。

7.2.1. 强制的服务依赖

如果应用上下文声明强制依赖OSGi服务,那么应用上下文的创建就会阻塞,直到所有的强制依赖都能在服务注册表中满足匹配。实践中,对于大多数用Gemini Blueprint服务构建的企业级应用,一旦平台和它安装的bundle都启动了,那么可用的服务就是达到一个稳态。在这样的世界中,简单地等待强制依赖的行为确保了bundle Abundle Bbundle A依赖bundle B导出的服务)以任意顺序启动。

等待强制依赖的超时时间要合适。默认情况下,超时是5分钟,但是这个值可以使用超时指令配置。参见第8.1节“Bundle格式和Manifest头”了解细节。

Blueprint用户通过在Bundle-SymbolicName 中声明blueprint.timeout属性可以获得同样的结果。

可以修改应用上下文创建的语义,这样如果所有的强制服务不是立即启动的,那么应用上下文就会创建失败(参见前面章节了解更多信息)。再次强调,不管选择的那个配置,应用上下文创建失败不会影响bundle的状态。

要了解更多服务的可用性的信息,请参看第9.2.2节“导入服务可用性”

7.2.2. ApplicationContext服务发布

一旦bundle的应用上下文创建完成,应用上下文对象会自动导出为OSGi服务到OSGi服务注册表。这个上下文以接口org.springframework.context.ApplicationContext发布(也以所有可见的超接口和实现的类型发布)。这个发布的服务有一个服务属性名org.springframework.context.service.name,它的值被设置为bundlesymbolic name。如果是一个Blueprint bundle,那么这个容器也会以org.osgi.service.blueprint.container.BlueprintContainer发布,同时bundlesymbolic name也会以osgi.blueprint.container.symbolicname属性发布。

可以用bundlemanifest文件中的指令防止发布应用上下文。参见第8.1节“Bundle格式和Manifest头”了解细节。

注意

将应用上下文导出为服务,主要是用于单元测试和管理。在运行时访问上下文对象,调用getBean()方法或者类似的操作都是不允许的。访问在另一个应用上下文中定义的bean的偏好方式是将这个bean导出为OSGi服务,然后在需要访问这个服务的上下文中导入这个服务的引用。以这种方式过一遍服务注册表可以确保bean只能看到服务类型的兼容版本,同时遵循了OSGi平台的动态性。

7.3. Bundle的生命周期

OSGi是一个动态的平台:在框架的整个运行期间,bundle可能处于安装、启动、更新、停止和卸载等状态。

当一个处于active状态的bundle被停止时,它导出的任何服务会自动取消注册,bundle会返回到resolved状态。已经停止的bundle应该释放它获取的所有资源和线程。已经停止的bundle导出的package对于其它的bundle来说还是继续可用的。

处于resolved状态的bundle可以被卸载:而已经卸载的bundle导出的package对于其它的bundle来说继续可用(但是对新安装的bundle是不可用的)。处于resolved状态的bundle也可能被更新。更新过程从一个版本的bundle迁移到另一个版本的bundle

当前处于resolved状态的bundle可以启动,并转变到active状态。

下图说明了bundle的状态和它的转换过程:

Bundle States

OSGi PackageAdmin refreshPackages操作刷新整个OSGi框架或者指定的处于installed状态的bundlepackage。在刷新过程中,受影响的bundle的应用上下文会被停止被重启。refreshPackages操作之后,被更新的老版本的bundle导出的package或者已经卸载的bundle导出的package不再可用。请查阅OSGi规范了解完整细节。

当基于SpringBlueprint bundle被停止时,它的应用上下文会自动销毁。这个bundle导出的所有服务将会全部取消注册(从服务注册表中移除)。正常的应用上下文的拆除生命周期会被观察到(org.springframework.beans.factory.DisposableBean实现类和上下文中beandestroy-method回调将会被触发)

如果基于Springbundle停止后又重启,那么新的应用上下文会再次创建。

7.4. 资源抽象

Spring框架定义了资源抽象用于加载应用上下文中的资源(参看Spring资源抽象)。所有的资源加载都是通过org.springframework.core.io.ResourceLoader完成的。org.springframework.core.io.ResourceLoader对于bean也是可用的,可以用编程的方式加载资源。具有明确的前缀的资源路径-例如classpath:-对于所有的应用上下文类型都是统一对待的(例如web应用上下文和基于classpath的应用上下文)。相对的资源路径根据应用上下文的类型进行不同的解释。这让在最终的部署环境之外进行集成测试变得很容易。

OSGi 4.0.x规范定义了三种不同的资源加载空间。通过专有的OSGi特定应用上下文和前缀,Gemini Blueprint支持所有的空间:

7.1 OSGi资源搜索策略

OSGi搜索策略

前缀

解释

类空间

classpath:

搜索bundle的类加载器(所有导入的包和需要的bundle)。强制解析bundle。这个方法类似于Bundle#getResource(String)的语义

类空间

classpath*:

搜索bundle的类加载器(这个bundle和所有导入的包和需要bundle)。强制bundle解析。这个方法类似于Bundle#getResources(String)的语义。

JAR文件

(Jar空间)

osgibundlejar:

只搜索bundlejar,提供低级的访问,不需要解析bundle

Bundle空间

osgibundle:

搜索bundlejar和它附属的fragments。不创建类加载器,不强制bundle解析

 

请查阅OSGi规范的第4.3.12节了解这几种空间的不同解释。

[Note]

注意

如果没有指定前缀,那么就使用bundle的空间(osgibundle:)

[Note]

注意

由于OSGi动态的特性,bundle类路径在它的生命周期可以改变(例如当使用动态导入时)。当基于运行时环境或者目标平台做模式匹配时,这可能会引起返回不同的类路径资源。

所有正规的Spring资源前缀,例如file:http:,都是支持的,模式匹配的通配符也是支持的。使用这种前缀加载的资源可能来自任何位置,它们不会限制在资源加载的bundle或者它附属的fragments

OSGi平台可能会定义它们自己的唯一前缀来访问bundle的内容。例如Equinox定了bundleresource:bundlentry:前缀。这些特定平台的前缀也可能在Gemini Blueprint中使用。当然,代价就是绑定到特定的OSGi实现。

7.5. Bundle范围

Gemini Blueprint引入了新的bean范围,即bundle。这个范围与被导出为OSGi服务的bean有关,可以描述为每一个bundle一个实例(one instance per bundle)。导出为OSGi服务的bean,且具有bundle范围,它导致每一个bundle都会从服务注册表导入一个不同的实例。同一个bundle的消费者(不管是不是通过Gemini Blueprint定义)将会看到同一个实例。当一个bundle已经停止导入这个bundle时(不管什么原因),bean实例都会被丢弃。对于声明的bundlebundle范围的bean的行为就像是单例(即每一个bundle只有一个实例,包括声明的bundle)。这个合同生命周期类似于org.osgi.framework.ServiceFactory接口。

关于服务的发布和消费的更多信息,请参看第9服务注册表

[Important]

重要

只有声明的bean通过OSGi服务注册表被消费时,bundle的范围才是相关的。即只有当这个导出为OSGi服务的bean被其它的bundle请求和释放时,时,实例才会创建和销毁(跟踪)。

7.6. 访问BundleContext

通常,当使用Gemini Blueprint支持时,不需要依赖任何OSGiAPI。如果你需要访问OSGiBundleContext对象,那么Spring很容易实现。

Spring扩展器创建的OSGi应用上下文会自动包含一个类型为BundleContextbean,名字为bundleContext。你可以将这个bean的引用注入到应用上下文中的任何bean,通过名字或者类型。另外,Gemini Blueprint定义了接口org.eclipse.gemini.blueprint.context.BundleContextAware:

public interface BundleContextAware {

  public void setBundleContext(BundleContext context);

}

任何实现了这个接口的bean当用Spring配置时,都会注入BundleContext引用。如果你希望使用在bundle中使用这个功能,要在你的bundlemanifest中导入org.eclipse.gemini.blueprint.context包,因为如果不导入的话,你的bundle是看不见这个接口的。

7.7. 销毁ApplicationContext

应用上下文是绑定到bundle上的。因此,如果bundle关闭了(不管因为什么原因),应用上下文都会销毁,所有导出的服务都会被取消注册,所有导入的服务都丢弃。

与应用上下文的创建不同,它的销毁是以同步方式进行的,与停止bundle在同一个线程中。这就要求一旦bundle停止,它就不能再被使用(即使是类加载),防止应用上下文不能正确关闭。


Application Context Sequence Diagram

注意,一个bundle可以单独关闭,也可以关闭整个OSGi平台。如果当extender bundle被关闭,那么应用上下文将基于服务依赖以被托管的方式关闭。请看下一节了解更多细节。

7.8. 停止Extender Bundle

2.x中的关闭算法变化

Gemini Blueprint 1.0中的关闭算法实现被修订,更好的与Blueprint Container规范保持一致。即,前一个实现执行了一步(ordering),而后面的实现执行多个步骤来适应OSGi空间的服务变化。用户不会发现在运行时有任何不同,如果不是这样,请通知我们。

如果一个extender bundle停止了,那么他创建的所有应用上下文都会被销毁。这里描述的算法与Blueprint规范完全相同(121.3.11)。应用上下文按照下面的顺序关闭:

  1. 应用上下文没有导出任何服务,或者导出的服务没有被引用,那么而按照bundle id的逆序关闭。(最新按照的bundle的应用上下文最先关闭)
  2. 步骤1中关闭应用上下文可能会释放引用的上下文,这样需要额外关闭应用上下文,则重复步骤1
  3. 如果没有更多的活动应用上下文,我们就完成了。如果还有活动的应用上下文,那么一定存在循环依赖引用。求出每一个上下文导出的最高级的服务,那么这个循环就破解了,这个集合中最低级别的bundle被关闭(或者在并列的事件中,最大的服务id)。重复步骤1

 

阅读全文
0 0