SOA 核心技術及應用,第 7 章

isoa發表於2009-05-07

7.1 SCA 與 Spring

在 Java 企業應用 EJB 技術的反面,在 Java 企業應用的開源社群,Spring 可謂如日中天,“今天,你 Spring 了嗎”的問候語一度流行。當 SCA 遇到 Spring,是在春天綻放,還是被春風融化,沉醉不知歸路?在回答這個問題之前,需要對 Spring 有一個基本的認識。

7.1.1 Spring 的新主張

Rod Johnson 是 Spring 的締造者。在建立 Spring 之前,Rod 就是世界上頂尖的 Java、J2EE 開發專家之一。在 2003 年 2 月,Rod 出版了極其熱賣的一本書《J2EE 設計開發程式設計指南》(Expert One-on-One J2EE Design and Development),其中的示例程式碼,形成了一個開源的專案,名字叫 Spring。這是 2003 年春天的事。

Rod 是 JCP 的一員,是 Servlet2.4 和 JDO2.0 規範制定專家組的成員。長期從事諮詢行業的經驗,使得他能夠經常從客戶的角度而不是一個技術人員的角度去看待問題,更驅使他在深入研究 J2EE 後,對 J2EE 架構進行批判。

在《J2EE 設計開發程式設計指南》裡,Rod 指出傳統的 J2EE 架構過於臃腫,低效率,給關注它的人們帶來的優點和益處很有限。

Rod 不是光說不做的人。在指出傳統 J2EE 的諸多不變之後,Rod 給出他理想中的 EJB 應該是什麼樣子,並給出示例的程式碼。這些程式碼利用開源迅速成長,形成了現在的 Spring 專案。因此 Spring 就是 Rod 的主張。

Spring 認為傳統的 J2EE 是侵入式的,程式碼之間耦合嚴重。重耦合帶來兩個嚴重的問題:

(1)可重用性差;

(2)不容易實施單元測試。

EJB 被設計為一箇中介軟體,中介軟體主要目的就是為了被重用。一個可重用性差的中介軟體,其存在的合理性都有可能受到質疑。

而且,隨著軟體的規模變得越來越大,不容易實施單元測試的產品生命力就弱。在沒有單元測試保障的前提下,幾乎沒有程式設計師有能力和膽量往一個大規模的專案裡新增新的程式碼;即使斗膽新增了新的程式碼,也不能很快知道這些新的程式碼是否功能正確。新程式碼是否正確這個問題會留給系統測試人員、甚至使用者來回答。等程式設計師得到相應反饋,太長的時間已經過去,根本沒辦法隨需應變。那麼,怎麼辦?

Spring 認為應該解耦。解耦在軟體界是永恆的話題。解耦永遠正確,關鍵是如何解耦。為此 Rod 還找到一個解耦的利器:控制反轉模式(Inversion of Control,IoC)或叫依賴注入(Dependency Injection)模式。

什麼是控制反轉?Martin Fowler 在 2003 年在一篇文章中給出了清晰明瞭的闡述。Rod Johnson 也給出了容易理解的解釋。控制反轉的概念經常表述為好萊塢原則:“別給我打電話,我會打給你的”。這是經理人經常說的一句話。演員需要演出機會,導演需要演員。當導演需要演員時可以通過經理人找到演員。利用經理人,導演和演員解除了耦合。就程式設計而言,控制反轉將控制建立元件的職責搬進了框架,並把它從元件程式碼中脫離開來。比如元件 A 需要元件 B,元件 A 可以告訴框架它需要元件 B,框架直接把建立好的元件 B 返還給元件 A,不用元件 A 去操心如何建立元件 B 的細節。元件 A 和元件 B 的依賴是在執行時注入的,因此 Martin Fowler 認為,“控制反轉”這個名字太泛了,常常讓人有些迷惑,而依賴注入(Dependency Injection)更合適些。

元件和元件通過依賴注入解耦之後,單元測試會變得容易。一個元件的測試並不依賴於它所依賴的元件的完成。在一個元件所依賴的元件沒完工的情況下,可以寫 Mock Objects,在測試時動態注入,以完成真正意義上的單元測試。於是測試開發的迭代週期變短,節奏加快;程式碼的瑕疵迅速被發現;產品質量得到保障。

元件間的解耦還使得程式碼更容易重用。即使元件 A 依賴於元件 B,在依賴注入的框架下,也只是介面的依賴,元件 A 對元件 B 的實現一無所知;因此元件 B 的實現可以隨意替換而元件 A 的程式碼無須更改。

實現了依賴注入模式之後 Spring 就成了“好萊塢的經理人”。於是她開始到處找“導演”和“演員”。

春風喚醒了冬眠的熊。當 Spring 遇到 Hibernate,Spring 發現,Hibernate 出於不得已,把他的會話工廠(Session Factory)綁到了 JNDI 上。Spring 很輕鬆地將 Hibernate 從 JNDI 上面解下;並將 Hibernate 的會話處理拿出來共享。於是 Hibernate 這隻冬眠的熊開始發威了,不僅在中小企業應用中迅速風靡,而且在 EJB3 推出之際,成功取代 EJB2 的實體 Bean,成為 EJB3 裡新一代的 O/R Mapping 元件。

Spring 是讓已有的開源技術更加易用,因此除非不得已,其並不直接和其他的開源專案競爭。Spring 的位置是傳統 EJB 反叛者技術們的框架。因此這種策略至少在鬥爭哲學上,是很好的策略。

在 2004 年,Rod Johnson 又寫的一本更暢銷的書《J2EE without EJB》。封面和上一本書是一樣的。

他還成立了一家諮詢公司,叫 Interface21。Rod Johnson 把 Spring 的主要目的總結為兩條,使 J2EE 易用和促進良好的程式設計習慣。其中第一條通過依賴注入來實現,而第二條,通過面向介面程式設計和 Interface21 公司的日常業務來貫徹。當然也可以理解為一條,因為面向介面程式設計可以由依賴注入很自然就能得到的結果,而 Interface21 也是 Spring 背後的公司。

7.1.2 從 Spring 到 Tuscany

可以說,Spring 是面向介面程式設計的。

SCA 也可以說是面向介面程式設計的;但更精確的描述是,SCA 是面向服務程式設計的。

介面可以用來描述服務提供的內容,但不能描述服務提供的方式。一個服務,必須提供以下兩個資訊:

(1)服務的內容,有哪些操作,這些操作需要什麼引數,有什麼返回值,有什麼異常等;

(2)服務的提供方式,SCA 服務、Web 服務、SessionBean 還是 JMS 等。

服務的內容可以由介面來描述。在 SCA 裡,服務提供的方式用繫結來描述。如果不明確指出服務提供的方式,SCA 預設是 SCA 繫結。因此在 SCA 元件之間是面向服務的呼叫,只是在使用預設的 SCA 繫結的時候,看起來和麵向介面程式設計一樣。從這個層面看,面向介面程式設計是面向服務程式設計的最簡形式。

Spring 利用依賴注入,提供一個輕量級的框架,整齊劃一地使用現有技術,使現有開源 Java 技術更加容易使用。在使用其他技術這一點上,SCA 和 Spring 很類似。在 SCA 的 Java 實現裡,Tuscany 也利用了依賴注入,提供了一個架構,使 SCA 可以用統一的方式,也就是 SCA 的方式,來使用其他技術。但 SCA 不僅限於此。

SCA 不僅使其他技術一致而簡單地被使用(繫結用在引用裡),還提供一致而簡單的方式來構建其他技術(繫結用在服務裡),以使其他現有的技術來使用 SCA。從這個角度講,較之 Spring,SCA 是更為溫和的技術規範;它的出現不是為了取代什麼,更是為了增進和諧。

當然 SCA 是一個規範,其實現之一 Tuscany 就不僅限於 Java,到目前為止,還有相應 C++ 和 PHP 的實現。

SCA 利用其他技術還有另一種方式:實現。其他的技術可以以實現的身份進入 SCA。比如 SCA 遇到 Spring,就將其以實現的身份納入了 SCA 的體系。

7.1.3 在 SCA 裡使用 Spring



Spring 通過實現的方式融入 SCA 的體系。下面的例子還是計算器。這次把計算器用 Spring 實現。以下是 Spring 的應用上下文定義(calculator.xml):


 


實現和介面重用了以前的。於是這個 Spring 上下文就生成了。

把計算器元件修改一下:


	
 	
	
 
 	
	 	
 	
 	
 


其中 location 指向 Spring 應用程式上下文。下面是一個簡單的測試:

public class CalculatorTest {
	private SCADomain domain;

	@Before
	public void startSCADomain() {
		domain = SCADomain.newInstance("calculator.composite");
	}
	@Test
	public void testAddService(){
		AddService addService = domain.getService(AddService.class, 
		"CalculatorComponent/AddService");
		assertEquals(2.0, addService.add(1, 1));		
	}
	@After
	public void stopSCADomain() {
		domain.close();
	}
}

由這個例子還可以看出 Spring 裡的一些概念是和 SCA 裡的概念有對應的:

(1)Spring 裡應用程式上下文對應 SCA 裡的 Composite;

(2)Spring 裡的 Bean 對應 SCA 裡的元件;

(3)Spring 裡的 Bean 的介面對應 SCA 裡的服務;

(4)Spring 裡的實現類對應 SCA 裡的實現類。

這基本解決了如何在 SCA 裡使用 Spring 的問題。那下一個問題是,如何在 Spring 裡使用 SCA?

為解決這個問題,SCA 給出 Spring 擴充套件標記,使在 Spring 裡可以顯示宣告服務()、引用()和屬性()。這三個標記定義在:

xmlns:sca="http://www.springframework.org/schema/sca"

其中 用來 Spring 向 SCA 提供服務, 用在 Spring 裡引用 SCA 的服務。引用和屬性還是在執行時動態注入,其行為和 SCA 裡的引用與屬性沒什麼兩樣,因此不再舉例。

7.2 SCA 與 OSGi

在 Java 技術社群的裡面,洶湧澎湃著一股暖流。這股暖流企圖在 Java 技術的能力範圍之內,解決 Java 技術自身的一些問題,以提高 Java 技術的現有能力,擴大 Java 技術的能力範圍;之後在 Java 技術新的能力範圍之內,再解決新的問題,進一步提高 Java 技術的能力,進一步擴大 Java 技術本身的能力範圍;如此往復。這個流派就是 OSGi——利用溫和的改良方式推進 Java 技術的革命性變革,並取得了巨大的成就。

OSGi 以前的全名是 Open Services Gateway initiative,現在這個全名已廢棄。OSGi 聯合會不再為這個看似縮寫的名字作出進一步解釋。

7.2.1 OSGi 的成功

Java 作為一個物件導向的語言,違反了結構化設計的神聖準則:低耦合、高一致性。物件導向程式設計忙著在例項變數裡隱藏資料物件,為此還寫 get 和 set 方法,但忘記了整個的類結構都暴露給了其他的類。比如類 A 和類 B,如果在類 A 裡有一行 new B(),那類 A 就整個依賴類 B 了,沒有類 B,類 A 就執行不了。類 B 再怎麼隱藏自己的資料物件也沒用,因為類 B 已經整個地暴露在類 A 面前了。

這種強耦合導致包依賴問題。Java 開源專案的各個庫之間,依賴關係非常嚴重,以至於專門成立了一個叫 Maven 的專案,來解決 Java 的包依賴。下面是一個基於 Tuscany SCA 的類似 HelloWorld 的程式,其定義在 Maven pom.xml 裡的依賴關係:

tuscany-host-webapp
tuscany-implementation-java-runtime
tuscany-binding-ws-axis2
tuscany-http-tomcat

只依賴於 4 個包並不算多,但這 4 個包又依賴於另外 64 個包,由於太多,不一一列出。這 64 個包裡,一旦哪個包的一個類發生變化,就會導致 Tuscany 不工作。而這 64 個包是由很多不同的團隊維護,類變化是很正常的事。這不是 Tuscany 的問題,這是物件導向開發的通病。

Java 釋出的時候給出三個發行版:JavaEE、JavaSE 和 JavaME,分別對應企業應用、桌面應用和嵌入式應用。Sun 企圖通過提供不同的 Java 版本來滿足不同的需要,而這實際上還是違反了神聖原則的第二條,高一致性原則;這個分堆降低了一致性。

OSGi 秉承鬆耦合髙一致性的原則來解決 Java 的重用問題。OSGi 提供了一個模組框架,這個框架可以管理模組,可以動態載入模組;模組之間通過服務介面協作。模組在 OSGi 裡被叫做 Bundle。OSGi 使用微核心 + 系統 Bundle+ 應用 Bundle 設計。微核心保證了髙一致性,基於 Bundle 的設計保證了鬆耦合。

OSGi 主要做三件事:

(1)Bundle 管理;

(2)Bundle 的動態載入;

(3)保證 Bundle 間使用服務機制來互動。

OSGi 最初是為嵌入式裝置設計的。嵌入式裝置對模組的重用要求很高。跑在豪華型轎車裡的模組可以不加修改地跑在經濟型轎車裡。這樣可以節約很多開發成本。OSGi 用模組框架成功地保證了這一點。BMW 汽車的應用控制系統採用 OSGi 作為其底層架構。

真正使 OSGi 引起大眾關注的是 Eclipse。Eclipse 3.0 平臺是 OSGi 的一種實現(就像 Emacs 是 lisp 語言的解釋執行器一樣),每一個 Eclipse 外掛,實際上在內部都使用了 OSGi。

Eclipse 的外掛體系結構,源自於 Visual Age for Java 的血統,在整個業界非常有名,被認為是非常成功的一種設計。但 Eclipse 在 3.0 版本時,專案組卻做了一個重大決定,就是拋棄 Eclipse 自己以前的外掛體系架構,轉為直接使用 OSGi 作為其外掛體系結構。這是為什麼呢?

Eclipse 的外掛體系結構和 OSGi 的思想非常類似,都強調微核、系統外掛、應用外掛的概念。Eclipse 的外掛體系比 OSGi 在外掛或 Bundle 管理上稍差一籌。隨著 Eclipse 外掛的增多,Eclipse 的啟動速度越來越慢。Eclipse 期望 OSGi 能解決它的效能問題。在外掛的動態載入上,Eclipse 外掛體系也落下風。在 Eclipse 3.0 之前,安裝完外掛 Eclipse 都要重啟外掛才能生效。Eclipse 還想得到 OSGi 的外掛動態載入的好處。關於外掛之間的互動方面,Eclipse 和 OSGi 有不同。Eclipse 的外掛使用擴充套件、擴充套件點來互動;而 OSGi 使用服務註冊、尋找、繫結來互動。這時候 OSGi 的微核心 + 系統 Bundle+ 應用 Bundle 的好處顯現了。Eclipse 成功地基於 OSGi 開發出擴充套件和擴充套件點的 Bundle;基於 OSGi 自身滿足了自己的需求。

Eclipse 採用 OSGi 作為其外掛體系結構的成功是很明顯的,在 Eclipse 3.1 版本以後可以明顯地感覺到啟動速度的提升,同時也使得可以在執行時對外掛進行管理,更明顯地提升使外掛的開發更加規範,從而可以使用很多已有的 OSGi 外掛。Eclipse 也向 OSGi 社群貢獻了自己的 Bundle。

Eclipse 的 Bundle 隨即被用到了 IBM WAS6.1 裡。一切都是順理成章的事。IBM WAS6.1 由此基於 OSGi。Eclipse 的成功轉型為 WAS 6.1 的轉型掃清了主要障礙。

7.2.2 OSGi 與 SCA 的異同

OSGi 關注軟體系統內部的模組化。OSGi 認為,軟體系統是由一個一個的 Bundle 組成的;這些 Bundle 的重新組合可以形成新的軟體系統。模組化是為了 Bundle 的重用。Bundle 的動態載入使執行時替換 Bundle 成為可能。Bundle 間通過服務互動使 Bundle 之間的耦合很鬆。

SCA 關注服務的使用和實現。通過一致的方式使用服務,也通過一致的方式實現服務。SCA 將服務和服務的實現解耦,並將服務的介面和服務的型別解耦。

“服務”在 OSGi 和 SCA 裡都出現但意義不同。OSGi 裡的服務是從技術上講的服務。SCA 裡的服務是從業務上講的服務。技術上的服務和業務上的服務概念一致但粒度不同。OSGi 的服務是細粒度的,而 SCA 的服務是粗粒度的。

因此從理論上講,SCA 的實現,至少是 Java 實現,應該基於 OSGi。業務隨需應變的根基是技術上的可重用。如果 SCA 的實現一旦基於 OSGi,就等於插上了可重用翅膀。

7.2.3 SCA 和 OSGi 的關係

總結起來,OSGi 可以以下列 3 種方式進入 SCA:

1.OSGi 繫結(binding.osgi)

像對待 Web 服務、會話 Bean 一樣,將 OSGi 看做和 SCA 對等的技術,實現兩種技術的互訪問。

2.OSGi 作為 SCA 的一個實現(implementation.osgi)

把 OSGi 作為 SCA 的一個實現,像 Spring、BPEL 進入 SCA 的方式一樣。

3.OSGi Host

用 OSGi 來實現 SCA,SCA 作為 OSGi 的一套 Bundle 來執行。

這三種方式中,第三種方式即 OSGi Host 的方式,是較為理想的。作為一個元件程式設計模型,SCA 的核心部分可以使用 OSGi 實現;擴充套件部分使用 SCA 自身的元件程式設計模型實現自實現。SCA 的另一個開源實現,Newton(http://newton.codecauldron.org/),就使用 OSGi 來構建 SCA,採用的是第三種方式。Newton 的 SCA 實現證實了第三種方式的可行性,其實現過程中積累的經驗教訓也可以為將來的實現者所借鑑。

SCA 和 OSGi 中的服務的粒度並不同。SCA 中的服務粒度較粗,OSGi 中的服務粒度較細。OSGi 更適合軟體內部的模組化。將 SCA 與 OSGi 看做對等的技術實現互訪問不是很可取。因此第一種方式只是作為一個參考。

第二種方式對 Tuscany 的 SCA 實現而言是比較現實的方式,實現起來也較容易。當下 Tuscany SCA 的 Java 實現使用第二種方式來歡迎 OSGi,即以“implementation.osgi”的方式將 OSGi 引入 SCA。Tuscany 目前採用微核心加可擴充套件外掛的架構,這些可擴充套件外掛是由 SCA 元件組成。從這一點上可以講 Tuscany 是自實現的。這和 Eclispe 的情形很像。就當前 Tuscany 的實現來講,其與其他技術耦合過重的跡象已經顯現。因此如果 Tuscany 的 SCA Java 實現也會以 Bundle 的方式來融入 OSGi,可能會部分地解決這個問題。

7.3 SCA 與 SDO

就像 Web 服務與 SOAP 的關係一樣,SCA 還有一個親密夥伴——SDO(Service Data Objects,服務資料物件)。作為夥伴,兩者互補,相互協作。SCA 關注服務的整合,在服務整合裡,資料的整合是關鍵,這正是 SDO 所關注的。

7.3.1 SDO 簡介

SDO 的目的是把開發人員從處理資料的底層技術中解放出來,從而更關注業務邏輯。放在 SOA 的大框架下,SCA 關注服務,不同的服務用一致的方式來使用,並可以用一致的方式來構建;BPEL 關注流程,把各個服務按需要串起來;SDO 關注資料,資料整合在 SDO 之下,這樣 SDO 資料就可以像血液一樣,在 BPEL 流程裡無阻礙地流動。

Tuscany 裡有 SDO 的一個 Java 參考實現。如果要使用,可以在 pom.xml 里加入如下依賴:


 org.apache.tuscany.sdo
 tuscany-sdo-impl
 1.0-incubating
 runtime


SDO 推薦使用 XSD 來定義資料物件,比如用 XSD 定義一個訂單(Order)如下:


 
 	
 	 
	 
	 
	 
	 
 	
 


這個簡單的業務物件描述了一個訂單:包括客戶需要的產品、數量以及發貨地址等。這些描述跟技術無關,而是面向業務的。

使用 XSD 描述資料物件有很多好處。其中之一是可以重用已有的工業標準。在 XML 的應用過程中,工業的各個領域用 XSD 設計了大量的、極其精細的資料描述。這是一筆財富。因此採用 XSD 來描述資料物件很自然地繼承了這筆財富。

其次 XSD 圖形介面的編輯器並不缺乏。這些編輯器可以繼續使用。比如本例使用 Eclipse 裡的 XML Schema Editor 看起來是這樣的:



藉助於編輯器,業務人員設計這些資料物件會更在行,而且要比技術人員設計的更貼近業務和客戶的需求。

Tuscany 裡 SDO 的使用並不複雜。下面是一個簡單的例子,展示如何使用;同時也是一個單元測試,以表明單元測試即使對資料物件的設計來講,也是必要的。

@Test
public void testOrder(){
	List list = XSDHelper.INSTANCE.define(
	ClassLoader.getSystemResourceAsStream("Order.xsd"), 
	null);
	assertNotNull(list);
	assertEquals(1, list.size());
	DataGraph dataGraph = SDOUtil.createDataGraph();
	assertNotNull(dataGraph);
	DataObject rder = dataGraph.createRootObject("
	 http://samples. hex/Order", "Order");
	assertNotNull(order);
	order.setString("name", "whoami");
}

最後一行的賦值表明了一個技術人員對實際業務知識的缺乏:他甚至不知如何給一個訂單一個合適的名字,因此鍵入了一個 UNIX 命令權且測試。如果一個真正的業務人員,估計會這麼寫:

order.setString("name", "TP-1248-9987");

資料物件可以序列化為 XML。這又得到了 XML 跨平臺、跨語言特性的好處。

更詳盡的關於 SDO 的介紹參見第 8 至第 13 章。

7.3.2 SCA 裡使用 SDO

SDO 和 SCA 幾乎同時出生。之後 SDO 很快地從 IBM 釋出,進入公眾視野,並形成一個規範,並且 Eclipse 很快在 EMF 裡部分實現了 SDO 規範。在 Tuscany 的 SDO 實現也是圍繞 EMF 的 SDO 實現展開。SCA 沒那麼快進入公眾視野。SCA 的最初實現在 IBM 內部完成。IBM 確認 SCA 的方案可行之後,才將其公佈。

在 SCA 裡使用 SDO 並沒有什麼特別。介面的定義使用 DataObject 作為引數和返回值。

import commonj.sdo.DataObject;

public interface OrderService {
	public DataObject createOrder();
	public void updateOrder(DataObject order);
}

Java 的介面的定義很自然。引數型別和返回值型別選用 commonj.sdo.DataObject 就是了。

在 WSDL 介面的定義裡,在定義 WSDL 型別的時候把 XSD 裡定義的型別用上就可以了。



 



其他的部分保持不變。這也是 SDO 使用 XSD 作為定義而得來的好處。

7.3.3 資料整合和服務整合的目的

為什麼用 SDO 進行資料整合?可以回答是為了 SCA 更好地進行服務的整合。那服務整合的目的又何在?當然不能回答是為了資料整合了。

服務整合的目的是為了流程的整合。真正的商業流程是一系列的、各種型別的服務的呼叫。業務邏輯也反映在商業流程中。當一系列服務匯入流程的時候,應該儘可能地把技術細節遮蔽掉,不能讓技術細節干擾了業務邏輯的編寫。

業務和技術分離不能只是一句口號。業務和技術的分離同樣需要技術的保障。這個技術的保障就是 SOA 系列技術。可以講 SOA 技術的目標之一就是保證業務和技術的分離。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14780828/viewspace-594430/,如需轉載,請註明出處,否則將追究法律責任。

相關文章