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

檢視所有