将 Spring 引入蓝图

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

上个月,在 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 都可以在同一个应用程序中支持这两种风格,从而提供更大的灵活性。例如,字段注入或基于注解的配置可以轻松添加到Blueprint bundle中,就像在传统的Spring应用程序中一样,从而补充了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,以便将实现与其公共契约(接口)隔离。我们不会手动创建bundle清单,而是使用Bundlor来自动生成它。只需将internal包标记为私有。
Excluded-Exports: *.internal*

请注意,模板中没有关于注解或配置的信息——由于Bundlor自1.0.0.M6起就支持Blueprint bundle,它会自动拾取并解析任何相关的配置和类。

步骤4:打包bundle

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

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

部署bundle

在部署bundle之前,您可以仔细检查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 bundle列表(通过在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

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

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

我们对未来的道路以及当前正在开发的功能感到非常兴奋!

有关OSGi和Spring DM的更多更新(和反馈!),请关注我们的博客和Twitter(通过标签#osgi#springdm#dmserver。您可以通过@costinl联系我)。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获得支持

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

了解更多

即将举行的活动

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

查看所有