将 Spring 集成到蓝图中

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

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

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

熟悉IoC 概念或 Spring 和 Spring DM 配置的用户会发现 Blueprint 规范很容易理解。事实上,由于它源自 Spring DM,许多 Blueprint 的概念、语法和术语都是相同的,在大多数情况下,在两者之间移植现有应用程序只需调整配置文件即可。在功能方面,Blueprint 提供了一个支持构造函数和 setter 注入、工厂方法、生命周期管理和回调、签名消除歧义和类型转换等的控制反转容器。在 OSGi 方面,可以使用导出器和导入器来透明地发布和使用 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 参考文档中的专用章节

混合搭配

自 Blueprint 诞生以来,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 捆绑包中,从而补充 Blueprint 的功能。

为此,让我们来看一下规范示例之一,并将 Blueprint 配置与JSR-250(常用注解)以及 Spring 3 中的一些新增内容(例如JSR-330(Java 依赖注入)支持)结合起来。

一个简单的例子

我从 Blueprint 规范(第 121 节,第 638 页)中选择了微不足道的回显服务示例,该示例显示了 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:更新清单

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

请注意,模板不包含有关注解或配置的信息——自 1.0.0.M6 以来,Bundlor 了解 Blueprint 捆绑包,它会自动拾取并解析任何相关的配置和类。

步骤 4:打包捆绑包

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

完成了。现在让我们运行我们的示例。

部署捆绑包

在部署捆绑包之前,您可以仔细检查 jar 内容(如清单),以确保它包含所需的一切。
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 捆绑包列表(通过在 equinox 中调用ss获得的结果)。

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

您可以在这里找到项目存档(包含说明)here

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

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

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

获取 Spring 电子报

通过 Spring 电子报保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进度。

了解更多

获得支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

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

查看全部