将 Spring 融入 Blueprint

工程 | Costin Leau | 2009年10月8日 | ...

上个月,在最初的 4.0 版本发布近 4 年后,OSGi 联盟正式批准了 OSGi 服务平台 4.2 版本。公告的标题突出了 Blueprint Container 服务,这是 Compendium 规范的新增内容,基于 Spring Dynamic Modules(也称为 Spring OSGi)项目推广的编程模型。为了快速总结 Blueprint,我将直接引用 OSGi 规范中的一段话:

(Blueprint Container)[...] 定义了一个依赖注入框架,专用于 OSGi bundle,它理解服务的独特动态性。它提供了一种 OSGi bundle 编程模型,该模型具有最少的实现依赖性,并且 Java 代码中几乎没有意外的复杂性。

熟悉 IoC 概念或 Spring 及 Spring DM 配置的用户会发现 Blueprint 规范易于理解。事实上,由于它派生自 Spring DM,许多 Blueprint 概念、语法和术语都是相同的,在大多数情况下,在两者之间移植现有应用程序只需调整配置文件即可。在功能方面,Blueprint 提供了一个控制反转容器,支持构造函数注入和 Setter 注入、工厂方法、生命周期管理和回调、签名消歧以及类型转换等功能。在 OSGi 方面,可以使用导出器(exporters)和导入器(importers)来透明地发布和消费 OSGi 服务。

下面是一个 Blueprint 配置的代码片段,它创建了几个对象,导入了一个服务,将它们连接在一起,然后将目标作为 OSGi 服务暴露出来:


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy">
    <!-- basic object creation -->
    <bean id="object" class="java.lang.Object"/>
    <bean id="length" class="java.lang.Integer">
        <argument value="4"/>
    </bean>
    
    <bean id="buffer" class="java.lang.StringBuffer" depends-on="simple">
    	   <property name="length" ref="length"/>
    </bean>
    
    <bean id="current-time" class="java.lang.System" factory-method="currentTimeMillis" scope="prototype"/>
    
    <bean id="list" class="java.util.ArrayList" destroy-method="clear" activation="eager">
    	   <argument ref="length"/>
    </bean>

    <!-- service import -->
    <reference id="ds" class="javax.sql.DataSource" filter="(batch-size=200)"/>

    <bean id="consumer" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
        <property name="dataSource" ref="ds"/>
    </bean>
    
    <!-- service export -->
    <service id="publisher" ref="consumer" auto-detect="interfaces"/>
</blueprint>

除了配置之外,Blueprint 还提供了一个小型的 API(通过 containerreflect 包)用于依赖查找、读取元数据或执行自定义类型转换,这在一定程度上类似于 Spring。有关 Blueprint 和 Spring DM 之间相似性(和差异性)的更多信息,请参阅 DM 2.0 M1 参考文档中的专门章节

混合搭配

Spring DM 主干自发布以来一直密切遵循规范,OSGi 4.2 平台批准后不久,继 Spring 3.0 RC1 发布公告之后,Spring Dynamic Modules 2.0 M1 发布了。Spring DM 2.x 作为 Blueprint 规范的参考实现(RI),我很高兴地报告,尽管这只是第一个里程碑版本,M1 提供了一个完整的 Blueprint 实现,完全通过了技术兼容性工具包(TCK)。

值得指出的是,尽管 Blueprint 依赖于 OSGi 4.2 API,但 Spring DM 2.x 依赖。运行 OSGi 4.0 和 4.1 的用户可以安全地使用 Spring DM 2.x;只有 Blueprint 功能会被禁用,其余功能仍然可用。

使用 Spring DM 的主要优势之一是,可以从 OSGi 透明地完全访问 Spring 容器:无论您打算使用 Blueprint、Spring/Spring DM API 和配置,Spring DM 2.x 都可以在同一个应用程序中同时支持这两种风格,从而提供更大的灵活性。例如,就像在传统的 Spring 应用程序中一样,可以轻松地向 Blueprint bundle 添加字段注入或基于注解的配置,从而补充 Blueprint 的功能。

为了说明这一点,让我们来看一个规范示例,并将 Blueprint 配置与 JSR-250(Common Annotations)以及 Spring 3 中的一些新增功能(例如 JSR-330(Java 依赖注入)支持)结合起来。

一个快速示例

我选择了 Blueprint 规范(第 121 节,第 638 页)中简单的 echo 服务示例,它展示了 bean/pojo 的基本注入和 OSGi 导出。

public interface Echo {
  public String echo(String m);
}

public class EchoImpl implements Echo {
  String message;
  public void setMessage(String m) {
    this.message= m;
  }
  public String echo(String s) { return message + s; }
}

<blueprint>
   <service id="echoService" interface="com.acme.Echo" ref="echo"/>
   <bean id="echo" class="com.acme.EchoImpl"
       <property name="message" value="Echo: "/>
   </bean>
</blueprint>

我将在这个示例中使用 Maven,并遵循其项目布局约定:[caption id="attachment_2939" align="aligncenter" width="326"]project maven layout[/caption]

步骤 1:添加注解

让我们修改EchoImpl类,以便它使用一个 OSGi 服务,例如PackageAdmin(为简单起见,因为大多数 OSGi 平台已开箱即用地提供此服务)通过上述注解在启动时为我们提供一些连接信息。

public class EchoImpl implements Echo {
	@Inject
	private PackageAdmin pkgAdmin;

	String message;

	public void setMessage(String m) {
		this.message = m;
	}

	public String echo(String s) {
		return message + s;
	}

	@PostConstruct
	void startup() {
		Bundle bnd = pkgAdmin.getBundle(getClass());
		ExportedPackage pkg = pkgAdmin.getExportedPackage(Echo.class.getPackage().getName());
		System.out.printf("Echo service bundle [%s] wired to bundles %s\n", bnd.getSymbolicName(), 
				Arrays.toString(pkg.getImportingBundles()));
	}
}

步骤 2:更新配置

要启用注解处理,只需使用context命名空间(有关更多信息,请参阅 Spring 文档中的本章节)。

<blueprint>
   <service id="echoService" interface="com.acme.Echo" ref="echo" />
   <bean id="echo" class="com.acme.internal.EchoImpl">
     <property name="message" value="Echo: "/>
   </bean>
   
   <reference id="pkgAdmin" 
		interface="org.osgi.service.packageadmin.PackageAdmin" />

   <context:annotation-config/>

</blueprint>

步骤 3:更新 manifest

一些人可能已经注意到,在步骤 1 和步骤 2 之间,我将 EchoImpl 类从com.acme移到了一个专门的包com.acme.internal中,以便实现与公共契约(接口)隔离开来。我们不手动创建 bundle manifest,而是使用 Bundlor 来自动生成它。只需将 internal 包标记为私有即可。
Excluded-Exports: *.internal*

注意,模板不包含关于注解或配置的任何信息——自 1.0.0.M6 版本以来,Bundlor 能够理解 Blueprint bundle,它会自动拾取并解析任何相关的配置和类。

步骤 4:打包 bundle

最后一步是简单地打包项目。我使用 Maven,但您可以轻松切换到 Ant 或其他构建环境。
# mvn package

大功告成。现在让我们运行示例。

部署 bundle

在部署 bundle 之前,您可以双重检查 jar 内容(例如 manifest),看看它是否包含所需的一切。
Manifest-Version: 1.0
Export-Package: com.acme;version="0.0.0"
Bundle-Name: blueprint-atinject
Bundle-ManifestVersion: 2
Bundle-SymbolicName: blueprint-atinject
Import-Package: javax.annotation,javax.inject,org.osgi.framework,org.o
 sgi.service.packageadmin

只需将生成的 jar 部署到 OSGi 4.2 框架中,同时部署 Spring DM 2.0.0.M1,您应该会看到以下输出:

INFO: Blueprint API detected; enabling Blueprint Container functionality
...
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
...
Echo service bundle [blueprint-atinject] wired to bundles []
...
INFO: Publishing service under classes [{com.acme.Echo}]

下面是我的 OSGi bundle 列表(通过调用ss命令在 equinox 中生成)。

0       ACTIVE      org.eclipse.osgi_3.5.0.v20090520
1       ACTIVE      com.springsource.slf4j.api_1.5.6
                    Fragments=2
2       RESOLVED    com.springsource.slf4j.juli_1.5.6
                    Master=1
3       ACTIVE      com.springsource.slf4j.org.apache.commons.logging_1.5.6
4       ACTIVE      com.springsource.org.aopalliance_1.0.0
5       ACTIVE      com.springsource.net.sf.cglib_2.1.3
6       ACTIVE      org.springframework.asm_3.0.0.RC1
7       ACTIVE      org.springframework.expression_3.0.0.RC1
8       ACTIVE      org.springframework.core_3.0.0.RC1
9       ACTIVE      org.springframework.beans_3.0.0.RC1
10      ACTIVE      org.springframework.aop_3.0.0.RC1
11      ACTIVE      org.springframework.context_3.0.0.RC1
12      ACTIVE      org.springframework.osgi.io_2.0.0.M1
13      ACTIVE      org.springframework.osgi.core_2.0.0.M1
14      ACTIVE      org.springframework.osgi.extender_2.0.0.M1
15      ACTIVE      com.springsource.javax.inject_0.9.0.PFD
16      ACTIVE      com.springsource.javax.annotation_1.0.0
17      ACTIVE      blueprint-atinject_0.0.0

您可以在此处找到项目档案(包含说明)。

通过在 OSGi 平台内采用事实标准(例如依赖注入),我们相信 Blueprint 对 OSGi 和非 OSGi 开发人员都有益,因为它鼓励 API 解耦和基础设施关注点的外部化,显著降低了创建和配置 OSGi 应用程序的入门门槛。

我们对未来的发展和当前正在开发的功能感到非常兴奋!

要获取有关 OSGi 和 Spring DM 的更多更新(和反馈!),请关注我们的博客和 Twitter(通过标签 #osgi#springdm#dmserver。本人在 @costinl 可用)。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅,为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举办的活动

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

查看全部