那么Spring-OSGi到底是怎么回事?

工程 | Costin Leau | 2007年4月5日 | ...

欢迎来到我的博客!这是我第一篇博文……有史以来第一篇。我一直抵制着写博客的冲动,但由于很多人鼓励我写我在i21所做的事情,我决定尝试一下。再加上Spring-OSGi昨晚(东欧时间)发布了第一个版本

我从去年八月开始参与 Spring-OSGi 项目,这段经历可谓跌宕起伏。这是我参与过的最具挑战性的项目之一,我很高兴它能发布,哪怕只是一个里程碑,向公众发布。非常感谢所有参与其中的人,特别是我的 团队 成员 - Adrian, Andy 和 Hal!

在这篇文章中,我想让您一窥 Spring 1.0 M1 目前提供的功能;我将跳过对 OSGi 的介绍,因为网上有大量优秀的资料(请参见底部的链接)。

Spring-OSGi 的基本理念是简化在 OSGi 环境中构建/编写/部署 Spring 应用程序。也就是说,让 Spring 提供的全面的 POJO 编程模型(IoC, AOP, 服务抽象)在一个以版本控制和模块化为重点的动态执行环境中无缝工作。

采用 OSGi 时最大的挑战之一是处理其动态性。服务(它们只是简单的对象实例)会来来往往,您的应用程序必须应对这种情况。解决方案并不直接,取决于具体情况,并且需要应用程序范围的考量,就像异常处理和事务一样。类加载限制,由前面提到的模块化强制执行,结合 AOP 可能会带来很多麻烦,迫使开发者创建“ hacks”,从而将 OSGi 提供的优势抛诸脑后。这些只是我们在 Spring-OSGi 中正在解决的 **一些** 问题的例子,最终应该能够实现 OSGi 的平滑过渡。

让我们来看一下 1.0 M1 中提供的一些功能

  1. OSGi Application Context

OSGi 基于 *bundles*,它们本质上就是带有特定清单条目的 jar 文件。它们是模块,是导出和导入类包和/或服务的单元。一个应用程序可以由一个或多个 bundle 组成。Spring-OSGi 提供了一个应用程序上下文,该上下文建立在 bundle 及其生命周期之上,让您可以访问应用程序所在的 OSGi 上下文,一个 OSGi 自定义 作用域以及一个 额外的 Aware 接口。像其他同类接口一样,该接口提供了依赖查找的功能,但您应该三思而后行,因为 OSGi 服务依赖注入是完全支持的。

  1. Resource abstraction

在 OSGi 中,类加载不再像以前一样了——例如,classpath 有了不同的含义,因为它可以从多个 bundle 中组合而成(这些 bundle 又可以用于一个以上的 classpath)。因此,getClass().getResource() 的结果可能会有所不同,因为您运行的环境已经发生了很大变化。下面是一个例子,说明您在查找一个类时可能会得到什么

Equinox: bundleentry://5/my/package/MyClass.class (也可能是 bundleresource://) Knopflerfish: bundle://13/my/package/MyClass.class Felix: bundle://18.0/0/my/package/MyClass.class

依赖 URL scheme 是不具有可移植性的,因此 Spring OSGi 做的第一件事就是通过简单而有效的 Resource 接口封装底层访问,这样无论您使用哪个 OSGi 实现,都可以找到您的文件。此外,还可以进行模式匹配查找,如 myFolder/*(实际上我们使用 /META-INF/spring/* 来检测‘spring 驱动’的 bundles)。

  1. Dynamic service support

假设您有以下应用程序上下文


<!-- service layer-->
<bean id="myService" class="ServiceClass">
    <property name="dao" ref="dao"/>
</bean>

<!-- dao layer -->
<bean id="dao" class="poorPerformerDAO">
    <property name="dataSource" ref="someDataSource"/>
</bean>

大多数应用程序都有几个层次,它们是 OSGi bundle 的绝佳候选者,因为您可以简单地将 DAO 类放在一个 bundle 中(dao bundle),将服务层放在另一个 bundle 中(service bundle),这样当 DAO 实现更新时(例如,上面的 *poorPerformerDAO* 被 *excellentPerformerDAO* 替换)或者部署了应用程序的不同版本时,无需重新启动应用程序:这是选择 OSGi 的最好理由之一!

然而,要利用 OSGi 的能力,对象必须成为服务——也就是说,它们必须在被“消费”之前注册到 OSGi 平台,而消费者(客户端)必须查找它们。这是一种 SOA 式的方法,避免了模块之间的紧耦合,因此当一个 bundle 关闭时,它发布的 خدمة 就会消失。这意味着您首先必须使用 OSGi API 进行注册和查找,同时还必须处理失败,因为服务可能会出现也可能消失。

Spring-OSGi 在这方面提供了极大的帮助,它允许您在不更改任何代码的情况下导出和导入任何类型的对象。

Service Bundle


<!-- service layer-->
<bean id="myService" class="ServiceClass">
    <property name="dao>
       <osgi:reference interface="daoInterface"/>
    </property>
</bean></code>

Dao Bundle


<!-- dao layer -->
<bean id="dao" class="goodPerformerDAO">
    <property name="dataSource" ref="someDataSource"/>
</bean>

<osgi:service ref="dao"/>

使用 Spring-OSGi 来适应 OSGI 环境,需要添加两行配置

  1. 来指示框架要查找哪个 OSGi 服务
  2. <li><span style="font-family:courier"><osgi:service ..></span> to export an existing bean as an OSGi service</li>
    

显然,对于 *dataSource* 依赖项也可以做同样的事情——将其外部化到一个 OSGi bundle 中,然后将直接的 ref 替换为 osgi:reference。无需处理新的 API,无需 try/catch/finally 异常,尤其是内置的查找行为。Spring-OSGi 可以被指示,直到找到一个实现 daoInterface 的服务,应用程序上下文才开始启动——也就是说,dao 依赖项可以得到满足。此外,在运行时,如果服务消失,Spring-OSGi 将根据您的配置(重试次数和超时时间)自动查找新的实现:如果在服务所属 bundle 更新时(例如,将 *goodPerformerDAO* 升级为 *excellentPerformerDAO*)调用了对 'dao' bean 的调用,您不会收到一个恼人的、看似无法解释的异常,而是会因为新的服务查找而产生一个微不可察的延迟。一如既往,该行为是完全可配置的。

在某种程度上,导出器/导入器功能类似于 Spring remoting,但有一个很大的区别,那就是没有 remoting——一切都在同一个 VM 中运行,而且根本没有序列化。

  1. Integration testing

测试很重要(甚至至关重要),尤其是在将应用程序迁移到新环境时,因为很多理所当然的事情可能会失败:我们在开发早期就亲身经历了这一点。这是一个大问题,因为在谈论 OSGi 时,测试既不轻松也无法自动化,因为需要启动(没有标准化的 API)执行环境(OSGi 平台或容器),并进行设置(安装测试所需的 bundle)。然而,棘手之处在于测试本身必须是 OSGified 的——放在一个声明了依赖关系的清单旁边,放入一个必须安装并启动到 OSGi 平台中的 bundle。

认识一下 AbstractOsgiTests & co


public class SimpleIntegrationTests extends AbstractConfigurableBundleCreatorTests
{

  public void testInstalledBundles() {
    // get access to the OSGi bundle context
     Bundle[] bundles = getBundleContext().getBundles();

     getBundleContext().installBundle(someBundleLocation);
     assertEquals(bundles.length()+1, getBundleContext().getBundles().length());
  }

  // specify the bundles to install  
  protected String[] getBundles() {
        return new String[] {
                "org.springframework.osgi, commons-collections.osgi, 3.2-SNAPSHOT",
                "org.springframework.osgi, org.springframework.osgi.test.simple.service,1.0-SNAPSHOT"};
    }
}

AbstractOsgiTests 构建在 JUnit 之上,因此您可以直接从 IDE 中编写和运行 OSGi 集成测试。整个设置由测试基础设施处理,您无需这样做:无需为您的测试编写 MANIFEST.MF,无需进行任何打包或部署——一切都自动处理。而且它速度很快,非常快!实际上,启动时间只有不到 10% 的时间花在 Spring-OSGi 代码内部——其余时间由 OSGi 平台本身使用。我们的大部分集成测试每个都完全在 1-3 秒内执行。Equinox、Knopflerfish 和 Felix 都得到了支持。

好了,我想第一篇文章的内容就够了……我将在未来的文章中写更多关于 Spring-OSGi 功能的内容。我希望我已经足够引起您的好奇心,让您尝试一下 1.0 M1(请注意,这是第一个里程碑,还有一些‘粗糙的地方’)。

感谢阅读!Costin

OSGi Alliance,它有一些不错的 介绍白皮书 Wikipedia EclipseCon OSGi 频道 Spring-OSGi 规范 Javapolis 2006 关于 Spring-OSGi 的 演示文稿(由我自己主讲)最后但同样重要的是,老朋友 Google

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有