學(xué)習(xí)如何使用應(yīng)用動(dòng)態(tài)代理模式(Dynamic Proxy Patterns)來(lái)進(jìn)行動(dòng)態(tài)解耦的 Web 服務(wù)適配器(Web Service Adapters)。通過(guò)適當(dāng)?shù)厥褂眠@種機(jī)制,您可以提供所需要的抽象級(jí)別,這樣有助于適當(dāng)?shù)拿嫦蚍?wù)的體系結(jié)構(gòu)(Service-Oriented Architecture,SOA)的實(shí)現(xiàn)與服務(wù)重用。
引言
理想的基于面向服務(wù)的體系結(jié)構(gòu)系統(tǒng)設(shè)計(jì)的需求是在服務(wù)消費(fèi)者與服務(wù)提供者之間進(jìn)行解耦。解耦可采用多種形式,其范圍從靜態(tài)(在編譯時(shí)通過(guò)請(qǐng)求端存根來(lái)解耦)到全動(dòng)態(tài)解耦(在所有服務(wù)調(diào)用的運(yùn)行時(shí)完全封裝并建立解耦)。顯然,全動(dòng)態(tài)解耦是一個(gè)嚴(yán)格的要求。如果一個(gè)服務(wù)被動(dòng)態(tài)解耦,然后服務(wù)特性中的更改導(dǎo)致了服務(wù)實(shí)現(xiàn)中的修正,但是所有其它的系統(tǒng)元素,特別是調(diào)用機(jī)制,仍舊保持不變。這種設(shè)計(jì)的優(yōu)點(diǎn)顯而易見(jiàn)。
對(duì)于 SOA 與 Web 服務(wù)的更強(qiáng)大功能與性能的日益增長(zhǎng)的期望與需求經(jīng)常導(dǎo)致顯著增長(zhǎng)的軟件復(fù)雜性。今天,SOA 實(shí)現(xiàn)的實(shí)際情況是,盡管規(guī)劃了服務(wù)的功能模塊化,但是它們?nèi)耘f經(jīng)常與獨(dú)立的中間件或通信協(xié)議(如基于 MOM 的 JMS、HTTP/SOAP 等等)耦合。在大多數(shù)組織中,當(dāng)應(yīng)用程序擴(kuò)展功能時(shí),開(kāi)發(fā)者關(guān)注的,在某種程度上,是靜態(tài)解耦(通常是指綁定),并強(qiáng)制為每一個(gè) Web 服務(wù)創(chuàng)建獨(dú)立的客戶端,而不是單獨(dú)的能夠同時(shí)訪問(wèn)多個(gè)服務(wù)的客戶端。
靜態(tài)綁定的局限性是非常明顯的——代碼中的靜態(tài)綁定阻止了不同應(yīng)用程序中貫穿整個(gè)企業(yè)的服務(wù)的重用。簡(jiǎn)而言之,即使需要提取并封裝所用的不同中間件及協(xié)議來(lái)執(zhí)行服務(wù)組件之間的交互,這些組件被包括在服務(wù)消費(fèi)者與服務(wù)提供者的交互中,這樣的需求非常普遍,但是封裝了 Web 服務(wù)接口的體系結(jié)構(gòu)還未得到廣泛應(yīng)用。造成這種狀態(tài)的其中一個(gè)主要原因就在于,盡管動(dòng)態(tài)綁定的幾種機(jī)制已被設(shè)計(jì)用來(lái)適應(yīng)這種局限性,但是對(duì)動(dòng)態(tài)綁定相關(guān)聯(lián)的實(shí)際技術(shù)依然存在很多的誤解。
Web 服務(wù)調(diào)用的剖析
已經(jīng)有許多關(guān)于 SOA 最重要的概念——服務(wù)接口(換句話說(shuō),調(diào)用功能)及在運(yùn)行時(shí)如何定位并調(diào)用它們的文章。換句話說(shuō), Web 服務(wù)調(diào)用應(yīng)該在后期綁定。需要指明的是 Web 服務(wù)調(diào)用已經(jīng)被設(shè)計(jì)為傳統(tǒng)的 Remote Procedure Call(RPC)方法,該方法準(zhǔn)許一個(gè)應(yīng)用程序調(diào)用由第二個(gè)應(yīng)用程序發(fā)布的功能。RPC 工作方式如下(如圖 1 所示):
- 被調(diào)用的應(yīng)用程序的功能是以本地功能的形式展示給調(diào)用的應(yīng)用程序的。
- 通過(guò)向調(diào)用的應(yīng)用程序提供功能存根達(dá)到定位透明性。
- 功能存根,當(dāng)它被調(diào)用時(shí),訪問(wèn)一個(gè)中間件層,該層向被調(diào)用的應(yīng)用程序傳輸調(diào)用及其相關(guān)的數(shù)據(jù)。
圖 1. 基于 RPC 的 Web 服務(wù)調(diào)用
在 Web 服務(wù)中,存根代碼通常是自動(dòng)產(chǎn)生的,并且是基于使用被調(diào)用的功能的接口描述,它通過(guò) Web 服務(wù)描述語(yǔ)言(Web Services Description Language,WSDL)來(lái)表示,并創(chuàng)建了存根 shell。此外,存根通常是在應(yīng)用程序開(kāi)發(fā)的編碼階段產(chǎn)生,因?yàn)榇娓侵饕菑膽?yīng)用程序代碼中直接調(diào)用,而且,結(jié)果是必須在編譯時(shí)解決的(換句話說(shuō),即所謂的前期綁定)。
大多數(shù)應(yīng)用程序開(kāi)發(fā)者,主要是 Java 開(kāi)發(fā)者,認(rèn)為使用這樣的存根是必要的,因?yàn)閷?duì)他們來(lái)說(shuō),如何處理從調(diào)用服務(wù)返回的復(fù)雜數(shù)據(jù)類型是不清楚的。當(dāng)然,來(lái)自被調(diào)用應(yīng)用程序功能的數(shù)據(jù)必須同 SOAP 消息相分離,而且,當(dāng)使用 Java 的時(shí)候,必須有一個(gè)相對(duì)應(yīng)的(兼容的)實(shí)現(xiàn)序列化接口的類。顯然,同樣的存根產(chǎn)生工具被用于產(chǎn)生那些所需求的類。
本文的首要思想就是,一般來(lái)說(shuō),通常自動(dòng)化是雙刃劍。一方面,自動(dòng)化準(zhǔn)許行業(yè)內(nèi) Web 服務(wù)的快速適應(yīng),其原因是,有了自動(dòng)化工具,大多數(shù)應(yīng)用程序開(kāi)發(fā)者的行為不再發(fā)生在 WSDL 自身的級(jí)別上。另一方面,不幸的是,市場(chǎng)仍需要一種主要的工具來(lái)選擇需要使用的調(diào)用形式——顯式的存根(存根在編譯時(shí)解析)或隱式的存根(存根在運(yùn)行時(shí)解析)。隱式的存根通常被當(dāng)成 Web 服務(wù)的無(wú)存根調(diào)用。
大多數(shù)基于 Java 的 Web 服務(wù)工具提供了一些 Web 服務(wù)接口的運(yùn)行時(shí)后期綁定功能。來(lái)自 webMethods 的 Glue 產(chǎn)品與 J2EE Web Services Developer 組件的 JAX-RPC 包就是兩個(gè)例子。然而,這些嘗試大多導(dǎo)致了軟件深受供應(yīng)商特定邏輯的影響。而且,問(wèn)題并不僅僅存在于供應(yīng)商指定的代碼。在許多情況下,供應(yīng)商專有的解決方案產(chǎn)生了實(shí)際的可管理性與可維護(hù)性的問(wèn)題。
現(xiàn)在讓我們回顧提到的問(wèn)題點(diǎn),例如用于后期綁定的 webMethods Glue 方式。目前,Glue 是廣泛使用的工具。由于使用了一個(gè)代理用于 Web 服務(wù)的接口,Glue 看起來(lái)好象是無(wú)所不能。然而,僅僅通過(guò)自身使用一個(gè)代理并不能消除所有的問(wèn)題。下面的 Java 代碼片斷描述了在它們的應(yīng)用中的一個(gè)典型的負(fù)面情形:
清單 1. lateBindingGlue
public class lateBindingGlue
{
public Document serviceInvocationGlue()
throws Throwable {
String wsdlName = "http://…../Service.wsdl?WSDL";
String operation = "……..";
String args[] = { };
/* first, Glue creates a SOAP interceptor */
SOAPInterceptor responseHandler = new SOAPInterceptor();
/* second, Glue registers the SOAP interceptor to catch incoming responses */
ApplicationContext.addInboundSoapResponseInterceptor(
(ISOAPInterceptor)responseHandler );
try {
/* third, Glue gets a proxy to the Web service through its WSDL */
IProxy proxy = Registry.bind( wsdlName );
/* here, service's operation is invoked through a proxy */
proxy.invoke( operation, args );
} catch( java.rmi.UnmarshalException e ) {
// Glue is catching the UnmarshalException that is perfectly expected
}
/* forth, Glue generates an XML document containing the SOAP body, */
/* and passes the whole document for parsing */
return new Document( responseHandler.getResponse() );
}
}
class SOAPInterceptor implements ISOAPInterceptor {
private Element soapBody;
public void intercept( SOAPMessage message,
Context messageContext ) {
try {
soapBody = message.getBody();
}
catch( Exception e ){
System.err.println( e.toString());
}
}
public Element getResponse(){
return soapBody;
}
}
|
您能從上面的例子中學(xué)到很多東西。首先,由 Glue 引入的代碼是一個(gè)兩層代碼。一層“消除”了 SOAP 分離的負(fù)面效應(yīng),通過(guò)使用 Java 異常處理程序和一個(gè)專門(mén)的組件——SOAP 攔截器來(lái)攔截這種特定的應(yīng)用程序異常而實(shí)現(xiàn)。第二層將服務(wù)調(diào)用的結(jié)果作為完整的 XML 文檔來(lái)提交給調(diào)用應(yīng)用程序。然后,XML 文檔通過(guò)使用一個(gè)諸如 DOM 結(jié)構(gòu)的工具進(jìn)行分離。顯然,這樣的方法不能“安全地”移植到其它 Web 服務(wù)工具中。但更重要的是,盡管攔截應(yīng)用程序異常的技術(shù)廣為人知,但它真的適合于 Web 服務(wù)調(diào)用嗎?
一般而言,異常是 Java 語(yǔ)言極為有用的特性,特別是,這意味著一種檢測(cè)錯(cuò)誤位置、無(wú)計(jì)劃的編程操作(如使用空指針)的簡(jiǎn)單方式。使用這種技術(shù)作為一個(gè)計(jì)劃的控制操作,而不是寫(xiě)一個(gè) if-then 邏輯段來(lái)測(cè)試一個(gè)指針是否真正為空,使代碼的可管理性與可維護(hù)性顯著地復(fù)雜化。顯然,SOAP 分離并不同于空指針。您不能通過(guò)一個(gè)簡(jiǎn)單的 if-then 代碼測(cè)試出來(lái)。不過(guò),它們的負(fù)面影響是相同的——拋出異常并捕獲它,這是一項(xiàng)代價(jià)非常高昂的技術(shù),特別是如果您需要獲得每秒數(shù)千次的 Web 服務(wù)調(diào)用的話。換句話說(shuō),運(yùn)行時(shí)異常應(yīng)該專門(mén)針對(duì)意外情況作為“防御線”而保留,以應(yīng)對(duì)軟件的錯(cuò)誤。
我進(jìn)一步指出為什么分離 SOAP 的問(wèn)題不能通過(guò)一個(gè) Java Exception API 進(jìn)行處理的更多原因。但是,讓我們先來(lái)看看引用的范例所顯現(xiàn)的另外一個(gè)問(wèn)題。由于調(diào)用操作需要直接分析 XML,它不得不訪問(wèn)響應(yīng)信息的 XSD 定義,而且必須依次成為提供的 WSDL 文件的一部分(且被存根產(chǎn)生工具所使用)。這里的最終結(jié)果就是甚至一些后期綁定的形式也存在于引例之中;在 Web 服務(wù)伙伴之間的緊耦合依舊存在。換句話說(shuō),Web 服務(wù)的客戶端必須訪問(wèn) WSDL 文件,并且在大多數(shù)情況下,它必須是一個(gè)完整(而不是部分)的 WSDL 文件。
還存在一個(gè)與使用 JAX-RPC API 相關(guān)的問(wèn)題。記住這是非常必要的:在 Java 語(yǔ)言中,由于在 XML schema 中的數(shù)據(jù)類型不能直接準(zhǔn)確地映射到 Java 語(yǔ)言的數(shù)據(jù)類型當(dāng)中,所以在調(diào)用的應(yīng)用程序端 WSDL 中產(chǎn)生的服務(wù)終端接口將與 JAX-RPC 編譯器在調(diào)用端產(chǎn)生的 WSDL 的形式不同。而且,生成的服務(wù)接口也將依賴于 SOAP 采用的編碼體制。例如,使用文檔文字編碼利用了每個(gè)方法類的封裝版本,并造成了額外的可管理性與可維護(hù)性的問(wèn)題。
Web 服務(wù)編程構(gòu)件的自由應(yīng)用程序邏輯:最佳實(shí)踐設(shè)計(jì)模式
根據(jù)到目前為止的講述,您容易想到使用任何種類的動(dòng)態(tài)調(diào)用技術(shù)(如后期綁定的動(dòng)態(tài)代理),不幸的是,許多開(kāi)發(fā)者都是這樣做的。關(guān)于 Web 服務(wù)其中一個(gè)最常見(jiàn)的誤解就是,使用動(dòng)態(tài)調(diào)用取代靜態(tài)存根的好處并沒(méi)有那么大,而且最好是堅(jiān)持用生成的存根作為 Web 服務(wù)調(diào)用的主要方法。
最后,我們的討論轉(zhuǎn)到第二個(gè),而且是最重要的原則——它不是真正的編程技術(shù),它確保不依賴服務(wù)編程構(gòu)件(如 WSDL),但是取而代之的是,它的價(jià)值體現(xiàn)在高級(jí)設(shè)計(jì)中。這對(duì)于獲得 SOA 的真正利益是必要的,因?yàn)殡S著服務(wù)的發(fā)展,服務(wù)消費(fèi)者的代碼將完全不受影響。無(wú)論如何,基于 SOA 的應(yīng)用程序必須注重于提供彈性體系結(jié)構(gòu),且更少的關(guān)注于公共通信協(xié)議(比如 SOAP)。服務(wù)與應(yīng)用之間不應(yīng)該具有嚴(yán)格的界限。一個(gè)終端用戶的應(yīng)用程序可以被看作是其它終端用戶的服務(wù)。整個(gè)企業(yè) IT 環(huán)境應(yīng)該被設(shè)計(jì)成高度模塊化,允許開(kāi)發(fā)者能夠挑選適合他們需要的服務(wù)與應(yīng)用組合。
在良好的 SOA 設(shè)計(jì)中,Web 服務(wù)消費(fèi)者的應(yīng)用邏輯能夠使用兩個(gè)基礎(chǔ)體系結(jié)構(gòu)原則來(lái)從服務(wù)構(gòu)件完全解耦:
- “保持可適應(yīng)性”——?jiǎng)?chuàng)建一種方法來(lái)支持多接口繼承(理論上,這將不受限制),在開(kāi)發(fā)有限數(shù)量的具體實(shí)現(xiàn)(在面向?qū)ο蟮脑O(shè)計(jì)當(dāng)中,經(jīng)常將匿名的內(nèi)部類作為對(duì)象適配器使用,考慮上下文,允許工作行為的定制,實(shí)際上是嵌入在子類之中)。
- “使用所謂的好萊塢原則:不要調(diào)用我們,我們將調(diào)用您”——Web 服務(wù)調(diào)用模型應(yīng)該只通過(guò)動(dòng)態(tài)代理框架來(lái)指定發(fā)現(xiàn),并假定客戶端事先對(duì)服務(wù)一無(wú)所知;因此,Web 服務(wù)提供方應(yīng)該能夠通過(guò)將存根類信息聲明到 XML 文檔,從而在運(yùn)行時(shí)宣傳他們的服務(wù)。那實(shí)際上就以一種開(kāi)放且可執(zhí)行的方式,提供了發(fā)現(xiàn)之外的另一種調(diào)用方法(換句話說(shuō),優(yōu)于 UDDI)。
最終設(shè)計(jì)目標(biāo)是,為 Web 服務(wù)消費(fèi)者應(yīng)用提供一組類,這些類根據(jù)上面引述的原則組成了可重用適配器層。這種適配器層封裝了使用存根及其生成的類的代碼。適配器的公共 API 并不公開(kāi)任何存根類;而是將其映射到 Web 服務(wù)消費(fèi)者應(yīng)用可以理解的類。
為了得到最好的適配器靈活性與可擴(kuò)展性,適配器的總體類結(jié)構(gòu)均通過(guò)使用下列設(shè)計(jì)模式的組合來(lái)構(gòu)建:
-
動(dòng)態(tài)代理是一種結(jié)構(gòu)模式,它定義了一組類與方法調(diào)用,這些類和調(diào)用被分派給調(diào)用句柄(在動(dòng)態(tài)代理類生成的時(shí)候指定)。使用動(dòng)態(tài)代理是 Java 編程中模擬多實(shí)現(xiàn)繼承的關(guān)鍵。通過(guò)動(dòng)態(tài)代理,定制的 InvocationHandler 能夠由一組表示合成子類的超類來(lái)構(gòu)建;該子類的接口將是這些超類實(shí)現(xiàn)的接口的聯(lián)合。
-
適配器是一種結(jié)構(gòu)模式,它通過(guò)對(duì)實(shí)現(xiàn)的抽象進(jìn)行解耦來(lái)影響類層次(比如繼承層次)的創(chuàng)建,因此兩者可以單獨(dú)改變。這種允許在運(yùn)行時(shí)選擇實(shí)現(xiàn)的解耦避免了抽象與其實(shí)現(xiàn)間的永久綁定。作為 Web 服務(wù)客戶端的調(diào)用應(yīng)用的類僅處理抽象。
-
服務(wù)配置器是一種行為模式,它使您可以改變適配器超時(shí)性能,并添加或去除附加功能,這樣就改變了調(diào)用框架的規(guī)范。例如,如果一個(gè) Web 服務(wù)提供方引入了一個(gè)新的協(xié)議(如在 RMI 上的 SOAP),那么它只需要“宣傳”新的傳輸性能。因此,適配器演示了這種新服務(wù)能力與這種調(diào)用功能的任何實(shí)現(xiàn)。使用一些元數(shù)據(jù)表示作為結(jié)果,必須具備一種方法能夠引用提供了獨(dú)立于特定規(guī)范與實(shí)現(xiàn)的功能類的接口。
-
工廠方法(Factory Method )是一種結(jié)構(gòu)模式,它使一個(gè)類延遲實(shí)例化到子類。在 Web 服務(wù)調(diào)用的情況下,本地與遠(yuǎn)程實(shí)現(xiàn)類都必須是子類,以實(shí)現(xiàn)它們的特定于服務(wù)的實(shí)現(xiàn)。需要訪問(wèn)服務(wù)的調(diào)用應(yīng)用將得到這個(gè)工廠的一個(gè)句柄,并給服務(wù)一個(gè)調(diào)用。因此,根據(jù)從服務(wù)配置器得來(lái)的信息,工廠封裝了訪問(wèn)服務(wù)必須使用的實(shí)現(xiàn)知識(shí)。
-
裝飾(Decorator)模式是一種結(jié)構(gòu)模式,它定義了緩存、發(fā)布與交換服務(wù)聲明的封裝器,用于連接合適服務(wù)與可重用的代理類。 通過(guò)使用裝飾模式,實(shí)現(xiàn)執(zhí)行調(diào)用的代碼與提供緩存的代碼相分離,將非常簡(jiǎn)單。
|
關(guān)鍵實(shí)現(xiàn)指南
圖 2
演示了使用上面引用的設(shè)計(jì)原則而設(shè)計(jì)的“好萊塢”類型適配器的概念模型。 圖 3 展示了使用適配器時(shí)的基本交互的序列圖。
圖 2. Web 服務(wù)適配器的概念模型
圖 3. 包含 Web 服務(wù)適配器的序列圖
創(chuàng)建一個(gè)使用適配器的服務(wù),包括創(chuàng)建一個(gè)使用服務(wù)描述符(代表 SOAP 服務(wù))的服務(wù)類。這個(gè)描述符被包括在服務(wù)類成員之中。服務(wù)類被作為 SOAP 來(lái)部署,例如,使用來(lái)自 Open Source Foundation 的 Axis。
服務(wù)描述符包括:
- 類名:包含服務(wù)實(shí)現(xiàn)的全限定類名的元素。
- 名稱:包含服務(wù)名的元素。
- 版本:包含服務(wù)版本名的元素。
- 創(chuàng)建者:包含服務(wù)創(chuàng)建者名稱的元素。
- AssertURI:包含指向一個(gè) XML 文件的統(tǒng)一資源標(biāo)識(shí)符(URI)的元素。其中,XML 文件包括服務(wù)聲明(規(guī)范)。
- 描述:包含服務(wù)描述的元素。
現(xiàn)在,有一個(gè)關(guān)于 Axis 的耦合注意事項(xiàng)。使用 Axis 作為 SOAP 引擎非常有助于實(shí)現(xiàn) Web 服務(wù)的松耦合,理由如下:
- 因?yàn)?Axis 定義了消息處理節(jié)點(diǎn),該節(jié)點(diǎn)能夠被封裝以供服務(wù)請(qǐng)求者(客戶端)與服務(wù)提供者(服務(wù)器)使用,適配器能夠使用部署描述符作為一個(gè) Web 服務(wù)部署描述符(WSDD),應(yīng)用一個(gè)消息處理節(jié)點(diǎn)來(lái)部署 SOAP 服務(wù)。
- Axis 中的服務(wù)通常是 SOAP 服務(wù)的實(shí)例,它包含請(qǐng)求與響應(yīng)鏈,但是必須包含實(shí)際服務(wù)類的提供方。結(jié)果是,通過(guò)將消息上下文傳遞給消息處理節(jié)點(diǎn)來(lái)完成 SOAP 服務(wù)的處理。
- Axis 客戶端處理能夠通過(guò)使用服務(wù)描述符和 XML 聲明文件創(chuàng)建包含服務(wù)細(xì)節(jié)的調(diào)用工廠實(shí)例,從而構(gòu)建調(diào)用對(duì)象。調(diào)用對(duì)象的屬性被設(shè)定為使用相關(guān)的目標(biāo)服務(wù)。然后,調(diào)用對(duì)象就通過(guò)調(diào)用 Service.createCall 工廠方法來(lái)創(chuàng)建。一旦建立起調(diào)用,
Call.SetOperation
就通過(guò)被調(diào)用的方法名稱來(lái)指定。然后,Call.invoke
通過(guò)相關(guān)的請(qǐng)求消息而被調(diào)用,它驅(qū)動(dòng) AxisClient.invoke
,處理結(jié)果消息上下文,并向客戶端返回響應(yīng)消息。
在適配器的設(shè)計(jì)中,應(yīng)該對(duì)性能進(jìn)行特殊考慮。使用動(dòng)態(tài)代理類具有性能含義。訪問(wèn)目標(biāo)的直接方法快于訪問(wèn)代理類的方法。然而,這不應(yīng)是在穩(wěn)健體系結(jié)構(gòu)與性能之間進(jìn)行選擇。這就是為什么目前適配器實(shí)現(xiàn)緩存封裝器的原因。通過(guò)應(yīng)用緩存,使用靜態(tài)存根與基于適配器的解決方案之間的差異相對(duì)較小。根據(jù)如何實(shí)現(xiàn)緩存,必須提到可能的解決方案之一——記憶(Memoization)。 記憶是一種廣泛使用的技術(shù),它在 Lisp、Python 與 Perl 這樣的功能編程語(yǔ)言中使用,給功能賦予預(yù)先計(jì)算的值。記憶一個(gè)功能將為功能添加一個(gè)透明的緩存封裝,因此已經(jīng)得到的值將從緩存中返回而不是每次都重建。記憶可以顯著提高動(dòng)態(tài)代理調(diào)用的性能。
總結(jié)我們關(guān)于此處描述的適配器的討論,允許支持本地和遠(yuǎn)程服務(wù)實(shí)現(xiàn)的設(shè)計(jì)是非常重要的。服務(wù)類對(duì)適配器來(lái)說(shuō)將是本地的,而遠(yuǎn)程 Web 服務(wù)則可以相互替換,因?yàn)榉?wù)類和代理類使用同樣的接口來(lái)訪問(wèn)遠(yuǎn)程 Web 服務(wù)。本地服務(wù)類將實(shí)現(xiàn)與 Web 服務(wù)器遠(yuǎn)程實(shí)現(xiàn)相類似的方法 getFunction()
來(lái)返回功能的結(jié)果。下面的 Java 代碼片斷進(jìn)一步說(shuō)明了這一點(diǎn):
清單 2. 本地服務(wù)類及其接口
public class LocalServiceImpl implements IService
{
/* get the service results */
public ….. getFunction()
{
………..
return …………;
}
}
|
實(shí)現(xiàn)了 IService 的代理類采用了方法 getFunction()
,但并未考慮該方法需要訪問(wèn)遠(yuǎn)程 Web 服務(wù)的代碼。這些代碼代表了需要用來(lái)訪問(wèn)部署在適配器內(nèi)的任意 Web 服務(wù)的代理代碼。
清單 3. 遠(yuǎn)程服務(wù)
public class RemoteServiceImpl implements IService
{
/* get the service outputs */
/* outputs are from the web service running in the location */
/* mentioned in the serviceURI */
public ….. getFunction()
{
adapter.examples.Function service = null;
String serviceURI = "http://……../Function/";
String wsdlURI = "http://localhost:8080/Function/";
Try
{
// init the lookup
WebServiceLookup lookup = (WebServiceLookup)
……….
// get the instance of the Web services interface
// from the lookup
service = (adapter.examples.Function)
lookup.lookup(wsdlURI,
adapter.examples.Function.class,
serviceURI);
}
catch (Exception e)
{
e.printStackTrace();
}
// now, call the methods on your Web services interface
return service.getFunction();
}
|
結(jié)束語(yǔ)
顯然,使用 Web 服務(wù)的靜態(tài)存根與前期綁定是 Web 服務(wù)調(diào)用的最簡(jiǎn)單的形式。但是簡(jiǎn)單化也帶來(lái)了明顯的缺點(diǎn)。與緊耦合方法不同,使用本文提出的適配器方法將給您留下可高度重用和可配置的 Web 服務(wù)代碼。由于 Web 服務(wù) 調(diào)用全部通過(guò)部署了服務(wù)描述符的公共適配器來(lái)引導(dǎo),因此您能夠動(dòng)態(tài)地決定調(diào)用什么服務(wù)——在部署代碼時(shí)或在運(yùn)行時(shí)。
同樣,那些可能尋找商業(yè)產(chǎn)品作為 Web 服務(wù)松耦合調(diào)用定制解決方案的組織應(yīng)該研究企業(yè)服務(wù)總線(Enterprise Service Bus,ESB)技術(shù)(參見(jiàn)參考資料),該技術(shù)提供了類似于上面所引述的功能的選項(xiàng)來(lái)連接企業(yè)內(nèi)部及跨企業(yè)的可作為 Web 服務(wù)的應(yīng)用程序,并具備一套功能來(lái)管理并監(jiān)視已連接應(yīng)用之間的交互。