今天我們開發(fā)的 J2EE 網(wǎng)絡應用程序,在表現(xiàn)層常用的就是Struts, Tapestry, WebWork, or Spring。這些工具一般使用MVC體系結(jié)構(gòu),輸出HTML到瀏覽器。典型的程序網(wǎng)絡開發(fā)模型就是要求 用戶對程序的每一個動作都要發(fā)送請求到服務器上。對于程序的每個用戶請求,服務器生成一個回復允許用戶提交一個新請求用以獲得更多信息。一個瀏覽器通常用來為用戶渲染界面。但是瀏覽器是一個有太多限制的客戶端,同時缺乏開發(fā)和用戶體驗。 富網(wǎng)絡應用程序(RIA)技術(shù)用來處理表現(xiàn)層的缺陷。這篇文章將以注重實踐的態(tài)度來理解什么是RIA,怎樣把它融入你的應用體系中。文章也將提出一種是跟幾個流行的 開源框架組合潛在的挑戰(zhàn)。 瀏覽器的局限 當前已有的解決方案的問題是什么呢?當程序行為正確時也許并沒有什么問題,可是曾經(jīng)大多數(shù)的網(wǎng)絡開發(fā)者都抱怨在使用瀏覽器作為客戶端的能力限制。這里有幾個當網(wǎng)絡應用程序使用瀏覽器產(chǎn)生的問題: l 各種瀏覽器以一些不協(xié)調(diào)的方式解釋象JavaScript這樣的腳本語言,這迫使開發(fā)者花費出幾倍的時間寫相同的代碼來適應不同的瀏覽器。 l 一些象標簽,向?qū)П韱危笮蛿?shù)據(jù)列表處理 等普通的功能卻困擾著開發(fā)人員并需要付出額外的實踐來為瀏覽器寫代碼。 l HTML本身就有局限性,靜態(tài)的標簽無法擴展。 l 用戶界面上的事件處理有著巨大的挑戰(zhàn),因為渲染HTML頁面只能同時顯示一個,事件無法不通過服務器更新其它頁面上的數(shù)據(jù)。 l 存儲程序狀態(tài)只能通過無法是用對象的cookies, l 使用瀏覽器幾乎無法開發(fā)需要脫機工作的程序 這些例子反復說明了大部分開發(fā)者都已知道的:當前的工具是由局限性的。開發(fā)者使用瀏覽器時經(jīng)常會因為這些問題要去找到解決方案。開發(fā)者和用戶都已經(jīng)對這種瘦客戶端的能力失去信心。 富網(wǎng)絡應用程序 有一種克服以上一些局限性的辦法,我們稱之為RIA,一個RIA 提供用戶一個擴展了瀏覽器無法實現(xiàn)的能力的胖客戶端。大多數(shù)普通的J2EE RIA客戶端是Java和flash。當需要開發(fā)一個以數(shù)據(jù)為中心的大型應用程序時,RIA通常比較適合。一些可用的開發(fā)RIA解決方案有 JDNC (JDesktop Network Components), Laszlo, Thinlet, Java Web Start, and Macromedia Flex。 RIA能解決上面所說的問題,以下是一些RIA能提供的功能列表: l RIA 提供類似于瀏覽器的 UI組件,增加了新本地化,更多組件。例如:包含數(shù)字遞進器,滑動控制,在線數(shù)據(jù)表格逐漸,和 菜單欄。 l RIA允許使用布局管理組件,象標簽向?qū)?,折疊欄,樹型,還有一些其它的布局,已經(jīng)接近AWT和Swing開發(fā)。 l RIA提供拖動-釋放 能力 l RIA中的語言風格對所有客戶端都是一致的,所以無需為不同應用重寫 l 請求/回復 模型不是所有用戶界面動作說必須的。使用RIA,用戶與界面交互只需要處理所需要的部分。RIA 可以使用HTTP協(xié)議方法上傳數(shù)據(jù)到應用程序服務器。通常,無論如何,首選的機制是Remoting,它支持不同的方式使用RIA。RIA 特別的使用一些擴展協(xié)議來在HTTP之上交流。 l 多組件事件處理可使用。 l RIA允許你存儲更多信息在客戶端代替httpsession,這減少了服務器的內(nèi)存消耗。 l 持久化狀態(tài),大多是在form對象,提供脫機時的能力 RIA是一個比較新的技術(shù),引進了新的觀念來發(fā)展這些類型的應用。它不是所有應用程序的銀彈,根據(jù)實現(xiàn),提供試驗。但是,假如你的應用能夠受益于富UI設計,那么RIA也許適合你。這篇文章現(xiàn)在將介紹一個RIA解決方案- Macromedia Flex,我們還將討論整合問題。 Macromedia Flex Macromedia Flex 是 一種提供RIA的商業(yè)表現(xiàn)層服務器。Flash插件是flex程序運行時環(huán)境所不可缺少的。大多數(shù)瀏覽器已經(jīng)預裝了flash插件,這將有助于你 立刻使用flex。我們將討論是用flash插件代替java插件與J2EE應用服務器交互的一些問題。 開發(fā)者使用兩種核心的語言來創(chuàng)建flex程序,第一個是MXML,Macromedia Flex標記語言,它包含了大量的XML標簽,允許開發(fā)者布局他們的界面。 MXML能引用到XUL,或者XML UI語言,這些標記可以被擴展,有程序需要的額外能力,不象HTML。其它MXML結(jié)構(gòu)允許你擁有自己的 look and feel MXML組件。 第二個語言是 ActionScript 2.0,它是一種ECMA-compliant 語言很像 JavaScript。ActionScript元素編碼在MXML頁面中。這是一種強類型的面向?qū)ο蟮恼Z言,很容易被熟悉 java的開發(fā)上手。ActionScript 還有豐富的事件處理能力允許應用程序響應動態(tài)的用戶交互。因為ActionScript 在flash插件中運行,所以無需為不同的瀏覽器編寫相似的代碼。 MXML跟ActionScript 都是基于文本的語言,能夠在普通的文本編輯器,一個象Eclipse 的IDE,或者其它成熟的工具 象flex builder。假如你有java,XML經(jīng)驗,或一些腳本語言象 javascript,你就只有很小的學習曲線學習flex開發(fā)。 Flex 服務器 是為了將MXML和ActionScript 編譯轉(zhuǎn)化成flash 字節(jié)碼.swf文件。這個過程就像java Web服務器容器編譯JSP文件成Servlet。Swf文件在客戶端的flash運行時環(huán)境中運行。Flex 服務器提供其它類似緩存,并發(fā),處理遠程對象的功能。 為你的遺留體系引入一個RIA框架 現(xiàn)在你已經(jīng)有一些RIA方面的理解了,讓我們來看看怎樣為你已經(jīng)存在的應用程序引入一個RIA框架。這將包括一些關(guān)于RIA應該在少耦合的程序中處于什么層的外置的討論。此外,討論將會在特別強調(diào)一些可能存在在開發(fā)flex與一些流行的開源框架結(jié)合的缺陷。這個例子會幫助你確定在引入RIA時潛在的問題。 讓我們開始標識一個分層的體系結(jié)構(gòu)。一個這樣的體系一般包括 表現(xiàn)層,業(yè)務委托層,業(yè)務綜合/服務層,和持久層。這是一個可能的個層實現(xiàn)關(guān)系: Flex + Business Delegates + Spring Framework + Hibernate 接下來的討論將集中在 綜合各個層。 關(guān)于我這個已經(jīng)存在的MVC表現(xiàn)層 表現(xiàn)層是一個web應用程序展現(xiàn)給用戶的界面,把請求轉(zhuǎn)發(fā)到服務器,存儲數(shù)據(jù)信息模型。剛開始的RIA開發(fā)者總是希望能重用已經(jīng)存在的Struts組件。但是象flex這樣的產(chǎn)品提供了他自己的MVC架構(gòu)。你會真的需要去維持并存兩個表現(xiàn)層框架在你的應用中嗎? 讓我們看看一個實際的例子中flex客戶端發(fā)出一個通過struts組件的請求到應用程序服務器上。一個來自flex的請求發(fā)送到struts表現(xiàn)框架之后被高層的程序接收。圖1顯示了一個這樣的例子 圖1 :怎么樣用其它java組件組合 flex 和 struts 象struts這樣的表現(xiàn)成框架在HTTP上傳送HTML請求,雖然我們可以使用HTTP協(xié)議,但更鼓勵開發(fā)者使用 remote object 調(diào)用,因此,使用這兩個表現(xiàn)層框架會逐漸產(chǎn)生協(xié)議不搭配,除非我們有一個特殊的需求直接用RIA組合struts。,避免他們。圖2 顯示一個更好的組合flex 和struts的方案: 圖2 ,介紹flex struts 平行存在于 java 組件中 圖2 提出了怎樣去隔離 struts 組件和flex 組件使他們共存。這滿足了應用程序需要平行的RIA組建和 輕量級 類struts組件。 開發(fā)者必須明白他們使用RIA客戶端是要做什么。這要求我們明確的轉(zhuǎn)換我們熟悉的傳統(tǒng)的請求/應答這種編程思想。象flex這樣的RIA產(chǎn)品不像struts這樣是 請求或者應答 驅(qū)動的。RIA 客戶端更新UI而無需全部的實例都返回服務器進行交互。 Struts 并不是你在使用RIA時需要唯一考慮的問題。我們只需花費一點時間就可以熟悉這些類型的技術(shù)。在這個學習路途中,最大的問題是組合java服務器端的組件。底線是不背離RIA觀念。 在業(yè)務層集成flex 現(xiàn)在我們了解了一些表現(xiàn)層技術(shù),讓我們來討論一下對程序結(jié)構(gòu)的其它層的影響。我們要重新配置我們的表現(xiàn)成組件,怎樣跟業(yè)務層組合在一起呢? Flex 是一個可擴展的RIA框架,提供了很多方法來與你的J2EE組件進行通信。Flex提供 HTTP 通信,web service通信,和 macromedia公司所特有的AMF(ActionScript Messaging Format)網(wǎng)關(guān)。AMF網(wǎng)關(guān)是一個高性能的二進制協(xié)議,就像flash里面的 remoting協(xié)議。Remote對象使用HTTP協(xié)議通過AMF網(wǎng)關(guān)傳遞信息。Flex為所有這些通信協(xié)議提供MXML標記,極大地減少了編碼復雜度。此外,flex允許你使用同步的或者異步的方式去調(diào)用遠程的業(yè)務層。當使用異步遠程調(diào)用時,用戶可以執(zhí)行一些客戶端行為,而不會有傳統(tǒng)web應用程序的阻塞現(xiàn)象。當然,你可以在適當?shù)牡胤绞褂猛椒椒▉碜枞脩舨僮鳌?span lang="EN-US"> 現(xiàn)在讓我們來考慮 怎樣在業(yè)務/綜合層中集成flex。討論中我們將使用spring框架作為我們的綜合層,當然這并不限制你使用任何你自己選擇的其它實現(xiàn)方式。讓我們假設你有一些服務操作在你的spring微容器中,而你需要在flex中調(diào)用這些遠程對象。 既然flex 無法直接知道任何關(guān)于spring的東西,所以你用一個擔當代理服務器組件的小層來分離他們。 另外,既然spring在java接口中有不俗的表現(xiàn),它也一定適合去構(gòu)建一些實現(xiàn)了一些spring服務接口的代理對象。這些代理對象將會提供一個在flex跟綜合層之間的分隔網(wǎng)關(guān)。唯一你需要去做的就是在flex配置文件中配置這些對象,然后你就可以通過AMF網(wǎng)關(guān)調(diào)用了。這里有一個關(guān)于在服務器端 flex-config.xml文件中怎樣配制代理對象的例子: <object name="OrderBusinessDelegate"> <source> com.meagle.flexro.FlexBusinessDelegate </source> <type>stateless-class</type> <use-custom-authentication> true </use-custom-authentication> <allow-unnamed-Access> false </allow-unnamed-access> <roles> <role>OrderUser</role> <role>Admin</role> </roles> </object> 第一眼你就可以看出來flex一些特別的能力,如配置安全選項,和確定代理對象是有狀態(tài)的還是無狀態(tài)的。當一個遠程對象調(diào)用一個綜合層對象時,調(diào)用會被flex代理java對象截獲。代理將會產(chǎn)生一個對綜合層或服務層的調(diào)用。結(jié)果對象將會被通過AMF網(wǎng)關(guān)發(fā)送回flex客戶端,在那里被解釋成actionscript對象。這里有一個MXML代碼使用遠程調(diào)用和存儲結(jié)果在數(shù)據(jù)模型中的例子: <mx:RemoteObject id="soapro" named="OrderBusinessDelegate" protocol="https" showBusyCursor="true"> <mx:method name="saveNewOrder" result="saveNewOrder_result(event)" fault="faultHandler(event.fault)"/> <mx:method name="findOrderById" result="findOrderById_result(event)" fault="faultHandler(event.fault)"/> <mx:method name="updateOrder" result="updateOrder_result(event)" fault="faultHandler(event.fault)"/> </mx:RemoteObject> <mx:Model id="roModel" > <!-- The object graph for the Order object will be stored here --> <Order/> </mx:Model> 域?qū)ο笫怯镁皖愃朴?span lang="EN-US">actionscritp 的java寫成,它來回于AMF網(wǎng)關(guān)。這個過程開始于一個來自于flex客戶端的請求,通過AMF網(wǎng)關(guān)到應用程序的其它層。結(jié)果對象圖表將會通過其他java層被發(fā)送回,最終通過AMF網(wǎng)關(guān)回到客戶端。當一個對象通過網(wǎng)關(guān)他會被轉(zhuǎn)換回actionscript等價物。圖3 顯示了這個過程 圖3 AMF 網(wǎng)關(guān)的 輪廓圖 其它的一些關(guān)于返回對象 通過flex和你的java層: 因為actionscript 2.0是一個面向?qū)ο蟮恼Z言,它可以創(chuàng)建actionscript的java等價物 對象。這樣就使得在對象來回穿越于AMF網(wǎng)關(guān)變得容易多了。Actionscript 對象被發(fā)送回flash插件類似于 數(shù)據(jù)轉(zhuǎn)換對象DTO,這是必須的,因為flash插件沒有任何java運行時組件。這里有一個熟悉的例子,一個java的命令 域?qū)ο螅?span lang="EN-US"> package com.meagle.bo; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This object represents an order. * @hibernate.class table="TestOrder" * @author meagle */ public class Order { private int id; private double total; private int version; private String userName; private List orderLineItems = new ArrayList(); // other fields and getter/setter methods not // shown to save space } 下面是一個 actionscript 等價物: /** * Generated Action Script Object * for com.meagle.bo.Order. Do not edit! */ class com.meagle.bo.Order extends Object { public function Order(){} public static var regClass = Object.reGISterClass("com.meagle.bo.Order", com.meagle.bo.Order); var id : Number; var orderLineItems : Array = new Array(); var total : Number; var userName : String; var version : Number; // other fields and getter/setter methods not //shown to save space } 你已經(jīng)注意到了在actionscript Order對象中有一個特殊的方法Object.registerClass()。這個Object.registerClass()方法是用來使AMF網(wǎng)關(guān)知道如何去耦合和解耦 java和actionscript 對象。這個方法注冊了 對應于 服務器端 java類的 客戶端actionscript 類。因為這些對象如此的相似,你無需去重寫你的域?qū)ο?,只需要一些細微的格式化。一些?span lang="EN-US">Xdoclet 和ant 這樣的工具允許你自動生成 這些 actionscript 對象?,F(xiàn)在你可以在flex 客戶端操縱你的java對象就像 actionscript一樣。 在持久成集成你的flex 在一個結(jié)構(gòu)良好的的網(wǎng)絡應用程序中,我們一般不直接跟持久層通信。使用flex不會改變這個架構(gòu)。大多數(shù)情況下綜合層會與你的持久層通訊。一般我們使用DAO 來訪問在一個象數(shù)據(jù)庫一樣的存儲地的數(shù)據(jù)。Flex 客戶端也不會直接與持久層通信,即使知道這些層的存在,因為這樣會產(chǎn)生強耦合。讓我們使用hibernate 來做持久成做一個范例吧。 使用hibernate和Macromedia的 AMF網(wǎng)關(guān)的遠程對象有兩個缺點。Hibernate用戶知道你不能訪問一個懶加載的集合collection,因為你還沒有在 session中初始化他們。訪問一個沒有被初始化的動態(tài)代理對象集合會導致運行時異常。AMF網(wǎng)關(guān)不知道如何去 尋找hibernate動態(tài)代理對象。一種可能的解決方案是面向方面的編程(AOP)攔截器,它能放置一個將要通過AMF網(wǎng)關(guān)的對象在一個委托對象中,然后移除動態(tài)代理。這個過程包括發(fā)送結(jié)果對象穿過一個遞歸查詢那些使用反射和沒有被初始化的代理對象的攔截器類。假如一些懶代理對象或者集合被發(fā)現(xiàn)了,他就會把他們的值設置為null。這是一個面向方面的一個截面,我們可以使用一些象 JBoss AOP, ASPectJ, Spring AOP等等 AOP語言來實現(xiàn)。AOP 攔截器將在業(yè)務代理層中應用。圖4 顯示了這樣的程序架構(gòu)的框架:
圖4 在通過AMF網(wǎng)關(guān)之前引入AOP攔截器和advice來代理對象. 跟進一步說,它會在一些像綜合層,持久層等地方減少耦合。 好消息是AMF 網(wǎng)關(guān)不知道如何你緩存雙向?qū)ο?,所以無止境的遞歸操作也不會在轉(zhuǎn)換對象過程中產(chǎn)生。因此你可以在傳送它們往返于AMF網(wǎng)關(guān)時保持這些完整的關(guān)系。 還有,因為對象是無連接和拷貝過來的,你必須使用Session.saveOrUpdateCopy(Object object) 方法持久化你的結(jié)構(gòu)到數(shù)據(jù)庫當中。這個方法必須被使用,因為對象在穿越AMF網(wǎng)關(guān)的過程中丟失了一些hibernate可以使用的特殊的字節(jié)碼信息。 認證 典型的 J2EE 應用程序有一些相同的認證設計。也許是 基于容器的認證模式或者是自定義的用戶認證代碼。像flex這樣的RIA服務器允許你在flash客戶端使用自定義認證表單,或者 在大多數(shù)應用程序服務器上基于容器的認證。此外,假如你要再上面的這個例子中查找業(yè)務委托配置,你就會注意到你能夠安全的為這些對象配置角色。經(jīng)常做的是在AMF網(wǎng)關(guān)中允許開發(fā)者獲取HttpRequest,HttpResponse和ServletConfig對象再你的代理對象方法中使用來增強安全性。 總結(jié) 這篇文章有目的的介紹了一些在使用象flex這樣的RIA時 進行的權(quán)衡和可能的缺陷。不管你是否使用flex或者其他RIA實現(xiàn),這里有些在使用這些技術(shù)主要的考慮的事情。當評價一個RIA框架,確定它在遇到一些問題時是否有足夠的可擴展性。另外,在RIA和java之間傳送數(shù)據(jù)需要注意小心評價綜合的問題。 資源 Mark Eagle is a Senior Software Engineer at MATRIX Resources, Inc. in Atlanta, GA. |
|