將 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 社群所有即將舉辦的活動。

檢視全部