1.背景、形勢 能夠進行Web開發的編程語言和技術很多 (1) 動態解釋語言 PHP; Perl; Python (Zope, Plone); Ruby (Ruby on Rails); (2) 編譯語言 Java; .net
Java Web開發遠非一枝獨秀: 除了受到來自.net 這個重量級對手的最大挑戰之外,更受到Zope, Ruby on Rail 等新式輕騎兵的沖擊(當然,也繼續受到老式輕步兵PHP, Perl的沖擊)。
官方Java走的是復雜路線,Servlet -> JSP -> Taglib。.net走的也是復雜路線,依靠成熟友好的集成化開發環境取勝。Java陣營好容易應對過來,從紛紜復雜的各種開發框架基礎上,發展出了重量級Web開發框架JSF,以及相應的集成化開發環境;渴望以此應對.net的攻勢。勝負未分,前途未卜。這時,另一個方向又殺來了新式輕騎Zope, Ruby on Rail。 Python, Ruby等動態解釋語言,面向對象特性更好,先天支持 動態綁定、AOP、函數式編程、“編程即配置”等時髦概念。開發速度更快,代碼量更小,達到killer級別。
傳統的HTML Web開發領域里面,Java已經是腹背受敵。領域外也展開了征戰,Rich Client Architecture的興起:AJAX(XMLHttp), Flash RIA, XUL, XAML, Smart Client(以及從前的ActiveX, Applet, Web Start)。
Web的發展趨勢是 語義Web,最終目的是讓整個Web成為一個巨大的數據庫。 這意味著,未來的Web應用將更加的面向文本內容數據,更加搜索引擎友好 – Search Engine Friendly. 二進制的客戶端插件,如Flash RIA, ActiveX, Applet, Web Start等,雖然交互性能最好,但不是以文本內容數據為中心,搜索引擎不友好。所以,我只是保持適當關注。我更關注基于文本的UI表現,如HTML, XUL, XAML等。XUL, XAML還沒有廣泛流行,只是保持一種有興趣的關注。 當下關注的重點,還是 XHTML + CSS + Javascript少量的 AJAX(XMLHttp)增加更好的交互性。
我一直認為:輕量、簡潔、高效 才是硬道理。后面闡述我對Java Web開發的理解和構想。
2. Web開發框架層次概述 從上到下,Web開發框架的層次如下: (1) HTML, JavaScript, CSS等頁面資源。 (2) 頁面模板層。 如JSP, Freemarker, Velocity, XSL,fastm等。用來生成HTML, JavaScript, CSS等頁面資源。 (3) Web框架。把HTTP Request調度分派到對應的Service Entry。 (4) Business Logic. (5) O/R Mapping. (6) JDBC (7) DB
根據我的經驗,一個典型的Web應用中的代碼比例如下: 頁面邏輯約占 50%,商業邏輯約占30%, O/R 約占20%。
但事實上,頁面卻是最不受重視的部分,從來都被認為是臟活,累活,雜活。典型的開發過程通常是這樣: 頁面設計人員迅速的用Dreamweaver等生成一堆文本雜亂無章的頁面,然后交給JSP程序員加入更加雜亂無章的Java代碼和Taglib。 當頁面布局風格需要改變的時候,頁面設計人員用Dreamweaver等生成一堆新的頁面。JSP程序員再重新加入更加雜亂無章的Java代碼Taglib。 至于頁面中的腳本邏輯調試,更是一門精深的工夫了。
根據社會規則,通常來說,工作內容越輕松,收入越高;工作內容越臟月累,收入越低;Web開發也是如此:做著最臟最累的活的頁面程序員,工資一般比不上后臺業務邏輯程序員。
開發框架通常會帶來這樣的結果:讓簡單的東西,變得更簡單;讓復雜的東西,變得更復雜。 這其中的原因在于: 一般來說,一個應用中簡單重復的東西占80%,復雜特殊的東西占20%。 簡單重復的東西很容易摸清規律,進行包裝,通用化。但是,在包裝的同時,經常就阻擋住了底層的一些靈活強大的控制能力。在復雜特殊的需求中,確實又需要這些底層控制能力,那么為了繞開框架的限制,付出的努力要比不用框架 大得多。 打個比方,一個比較極端的例子。編譯語言比匯編語言的開發效率高很多,但是卻無法直接操作寄存器。當需要在編譯語言中操作寄存器的時候,就非常的痛苦。比如Java,也許需要JNI,寫C代碼,還要在C代碼里面嵌入匯編。編譯、連接都很麻煩。 所以,一個框架的開發效率,就在于這個80%簡單 與 20%復雜之間的平衡。 假如,不用框架來開發,簡單的80%要消耗 80個資源數,復雜的20%要消耗20個資源數,總資源數是100;使用了某個框架,簡單的80%只要消耗10個資源數,復雜的20%要消耗40個資源數,總資源數是50。那么,我們說,這個開發框架是有效率的。
我的思路是,同時應對復雜和簡單。當然,為了應對復雜,簡單的東西可能就應對得不那么好。比如,做這樣一個開發框架,簡單的80%要消耗20個資源數,復雜的20%要消耗10個資源數,總資源數是30。 這種開發框架是有可能實現的。而且是很有意義的。尤其是在復雜部分的比例提高的時候。越復雜的系統,這種開發框架就越有意義。
后面的關于Web各層開發的論述,主要就按照這個“應對復雜、讓復雜更簡單”的思路展開。
3.頁面資源 也許有人會說,頁面資源,不就是HTML嗎?太簡單,太低極了,沒勁。Dreamweaver、Frontpage多簡單阿。隨便找個人來用就可以了。文本內容亂糟糟不要緊,瀏覽器里面顯示出來的效果好看就行。要增加炫的、酷的動畫效果,那就寫JavaScript唄。寫在HTML里面,看看在IE里面能不能運行就可以了唄。 這也正是大多數公司開發頁面資源的方式。因為頁面的需求變化是最多、最快的,而頁面的制作成本很低,人們不愿意在上面投入更多的資源。
我的看法是,萬丈高樓平地起。應用程序的每一個部分都應該完善管理,結構優美。越是需求變化多的地方,越是臟亂差的地方,越應該加大力度處理好。
頁面結構方面,Javaeye論壇的Dlee做了很多工作。
(1) 在 2005 年我們如何寫 JavaScript http://forum.javaeye.com/viewtopic.php?t=12973 (2)使用 Unordered Lists 制作的下拉菜單和樹 http://forum.javaeye.com/viewtopic.php?t=12995 從上面的Dlee的論述和給出的資料。可以看出,頁面資源分為三部分: (1) XHTML。結構,Structure。 XHTML里面的Tag部分只應該包括 <ul> <table> <p> <div><span>等結構布局Tag,或者<strong><emphasis>表示語義的Tag。 XHTML里面不應該包括風格信息,比如字體、顏色、大小、粗細等,也不應該包括<font> <b> <i> <h> 等字體信息。 XHTML里面不應該包括Javascript的定義和調用。
(2) JavaScript。行為,behavior。 JavaScritp應該存在于一個獨立于XHTML文件的獨立文件中。這樣可以做自動化單元測試。JavaScript應該只改變HTML DOM的結構和內容,而不應該改變它的風格。
(3) CSS。Style,風格。或者說,Presentation,表現。 前面說了,XHTML里面不應該包括JavaScript的調用。那么,XHTML的元素是如何JavaScript事件綁定起來?就是在CSS里面指定的。 當然,眾所周知,CSS的本職工作是處理頁面風格。
頁面資源方面,我完全認同Dlee的觀點。從技術和資源積累的長遠目標看來,這方面的初期投入的回報將是非常豐厚的。 即使將來HTML消亡了,進入了XAML, XUL, RSS時代,這些結構清晰的各部分,重用的可能性都非常巨大。JavaScript + CSS + XML UI的這種經典設計思路,將留存很久。混雜成一團的HTML的命運只能是全盤被拋棄。
4.頁面模板層 頁面模板層是指Server端運行的用來生成HTML(或JavaScript,CSS)的Server Side Template Engine。 這一層也是著名的臟亂差樓層。著名的HTML的Java代碼污染事件,就發生在這個樓層。不僅JSP有這個問題,其他的template, 如freemarker, velocity, tapestry等含有邏輯的腳本,都不同程度上有HTML的Script Logic污染問題。
Dlee的做法很美。直接就不要頁面模板層,不用Server Side Template Engine。直接用JavaScript更改HTML DOM的結構、內容、數據。同時,會用到少量的瀏覽器端XSL。 這樣帶來的結果,Template就是很干凈純粹的HTML,不含有任何Server Side Script。這個效果,和Servier Side Template 的 Jivan,XMLC達到的一樣。只是一個是在瀏覽器端執行,一個是在Server端執行。
我研究比較了幾乎所有的Server Side Template Engine,力圖采眾家之長,避眾家之短,寫了一個Server Side Template Engine -- fastm, 能夠最優雅方便的實現頁面模板層。關于fastm,我的Blog上有不少文章論述。 我的Blog,里面專門有個fastm 分類。 http://blog.csdn.net/buaawhl http://buaawhl.blogdriver.com
Fastm發布在java.net上。 https://fastm.dev.java.net
我仍然對Server Side Template Engine持肯定態度。基于如下原因: (1) JavaScript代碼量大、文件多的時候,不容易管理,不容易進行語法檢查,不容易跟蹤調試。 這里有人會爭辯,Server Side Template Engine也用到了很多腳本阿,比如Freemarker, Velocity, 而且嵌在HTML中,怎么管理,怎么調試?即使是JSP,也是Java Code嵌在HTML里面,怎么管理,怎么調試? 這里我要說,Jivan, XMLC, fastm,Wicket等Template Engine的邏輯都是在Java Code里面。
(2) 用JavaScript生成文本內容,搜索引擎不友好。 一般的網絡蜘蛛程序,只根據URL獲取HTML文本,搜索里面的文本內容,而不會執行里面的JavaScript腳本。
(3) JavaScript代碼重用還是有些局限 比如,有兩個HTML文件,一個是Table布局,一個是List布局。 我有同樣的一批數據,要在這兩種布局中顯示。 這時候,就要給這兩個HTML分別寫兩套JavaScript。這里面的DOM層次,元素,屬性都不同,再怎么定義ID,Class,也無法用完全相同的一套JavaScript處理。 這里有人會爭辯,Server Side Template Engine也無法做到。別說JSP, Velocity, Freemarker等要在兩套HTML里面嵌入相同的代碼,就是Jivan, XMLC, Wicket也要分別寫不同的兩套Java Code,因為它們的XML DOM Node / Model View (Table, List) 都是不同的。 這里我要說。fastm可以做到只用一套代碼邏輯。而且只有fastm可以。fastm的代碼重用率是最高的。
關于Ajax(XMLHttp),我的意見是必要時才用,而且最好采用粗粒度的用法 -- JavaScript發出一個URL請求,返回一整段HTML,直接替換到頁面的某一塊,而不是用JavaScript來做這樣的把數據填充到HTML DOM中。如果你直接在瀏覽器里面輸入那個URL,也可以獲取那整段的HTML內容。 典型的應用場合是Portal。Portal頁面的每個Portlet都含有這樣的 Ajax(XMLHttp) javascript代碼 -- 發出一個Portlet URL請求,返回一整段Portlet的內容,直接替換當前的Portlet塊。 這樣做的好處是: (1) 減少JavaScript代碼的量和復雜度。 (2) 搜索引擎友好。網絡蜘蛛程序可以辨別JavaScript中的URL,并根據這個URL,獲取整段處理好的HTML文本,進行內容搜索。 有人可能會爭辯:如果URL請求返回的是XML數據,不是整段處理好的HTML,搜索引擎也可以進行內容搜索。 這點我同意。前提是XML數據的內容是足夠連貫的,而不是散落的。比如,你返回的XML數據是“中國”。這個“中國”要放在HTML中的一個{country}位置,{country}足球。這個時候,結果HTML的內容含有“中國足球”。而XML數據中只含有“中國”。如果用戶用“中國足球”作為關鍵字來搜索,就找不到這個URL。
從前面給出的fastm資料的連接中,可以得知。如同Jivan, XMLC, Wicket一樣,fastm的template里面不含有邏輯,所有的邏輯都寫在Java里面。 有人會爭辯說:頁面邏輯寫在Java里面,我改變了頁面邏輯,還需要重新編譯。這也太不方便了。Velocity, Freemarker, JSP就不用重新編譯。 這里我的看法是:業務邏輯代碼改變了,不也需要重新編譯嗎?頁面邏輯就不是邏輯了嗎?HTML里面的腳本怎么語法檢查、跟蹤調試?業務邏輯需要語法檢查、跟蹤調試,頁面邏輯就不需要語法檢查、跟蹤調試了嗎? 對方可能會說:在我的應用中,頁面邏輯的改動需求非常頻繁,而且這些頁面邏輯非常簡單,不需要語法檢查、跟蹤調試。 這里我的意見是: (1) 那就使用JSP, Velocity, Freemarker等腳本。 (2) fastm, Jivan, XMLC, Wicket的Java代碼部分也可以寫在腳本里面,比如,Server Side JavaScript, Jython(Python), Groovy, Bean Shell 等腳本語言都可以很方便的和Java相互調用。
fastm的生命周期將很長。 HTML, XUL, XAML都是,或將是可以在瀏覽器或可視化編輯工具里面顯示的XML UI定義語言。Microsoft Office的Word, Excel, Powerpoint等格式都提供了相應的XML格式。這些XML文件都可以在Office里面顯示,并編輯。 Adobe公司也提供了PDF的XML格式 -- XDP。可以在Adobe Designer里面顯示并編輯。 由于fastm是Designer Friendly的XML UI所見即所得的模板技術。這方面具有很大的潛力。 根本不需要第三方花大力氣專門做個IDE,來顯示自定義的Tag。目標文件格式提供商自己的閱讀編輯工具就可以直接用了,而且效果就是運行后產生的結果文件的效果。
即使沒有可視化要求的場合。比如,Web Service需要的XML數據。fastm同樣有用武之地。比如, <!-- BEGIN DYNAMIC: users --> <user> <name>{name}</name> <address>{name}</address> </user> <!-- END DYNAMIC: users -->
可以很容易的把一個Java Object List轉化為XML數據。
另外,我不得不承認。瀏覽器端的JavaScript的頁面邏輯,可移植性要高于Server Side Template Engine。因為Server Side Template Engine通常是特定語言相關的。 目前fastm是用Java實現的。由于實現很簡單,移植到其它的語言,也很簡單。如果是移植到Python, Ruby等動態解釋語言,那就更簡單了。我是有這個考慮,因為Zope, Ruby on Rails 的模板還是Logic 和 HTML混雜的,fastm這個思路有很大的用武之地。
前面講了這么多。清理了兩層有名的臟亂差的老大難的樓層 -- 頁面資源層和頁面模板層。讓這兩層變得和下面的樓層同樣的優雅、清潔。
下面該講到Web框架層了。在向下講之前,由于前面提到了腳本,我想先插入一段關于“可配置”、“可編程”、“可熱部署”、“腳本邏輯 vs XML Tag邏輯”的話題。把這個人們比較關心、討論比較多的話題,先講清楚。
5.可配置、可編程、可熱部署、腳本邏輯 vs XML Tag邏輯 由于Java是編譯語言,人們通常把變化的參數部分抽取出來,放到配置文件中。 這些配置文件通常是XML文件。這很好,沒什么問題。XML很適合用來表達數據結構。 但是,對于某一種技術的狂熱,通常引起對這種技術的過度使用,或者誤用。 人們開始覺得,XML能夠表達一切東西,包括for, if, else等邏輯。這方面的典型例子有 Workflow XML Definition,Logic TagLib, XSL Logic Tag等。 這點我不敢茍同。我的看法是,XML不適合表達邏輯,XML表達邏輯非常蹩腳。XML表達邏輯相當于自定義一門XML格式的腳本語言。
比如,Logic Tablib,很難自然的支持 if else, switch。只能蹩腳地支持一堆 <logic:if> <logic:ifNot> <logic:exists> <logic:notExists> <logic:ifNull> <logic:notNull>。 (注,好久沒有接觸過Taglib了。這些Tag Name都是憑以前的使用印象寫的,也許名字不對,但表達這些意思的TagLib都還是有的) 如果要表達if () else if() else 就更蹩腳了。要進行非常麻煩的嵌套。
再比如,XSL 支持if, else 也非常蹩腳。非要多出來一個層次才行。 <xsl:choose> <xsl:when test="…"> …. If …. </xsl:when> <xsl:otherwise> … else … </xsl:otherwise> </xsl:choose>
同樣,如果要表達if () else if() else 就更蹩腳了。 <xsl:choose> <xsl:when test="…"> …. If …. </xsl:when> <xsl:otherwise> <xsl:choose> <xsl:when test="…"> …. If …. </xsl:when> <xsl:otherwise> … else … </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose>
可以看到,XML Tag 表達邏輯,非常麻煩,可讀性很差,完全是一種誤用,沒有半點優勢。當然,邏輯簡單的情況下,還是可以接受的。 有人會說:XML表達邏輯,可以免編譯阿。 那么我說:語法檢查呢,跟蹤調試呢? 對方說:只是一些簡單的邏輯,不需要語法檢查、跟蹤調試。 我說:如果只是為了免編譯,前面列出的那么多的解釋執行的腳本語言更適合。XML表達的邏輯,比Java等編譯語言還要麻煩很多,而腳本語言比Java等編譯語言簡潔多了,可讀性非常好,而且腳本語言和Java語言有很好的交互性,可以相互調用。重用、結構方面都具有優勢。
有人會舉出Spring IoC為例子,說:你看,Spring IoC的配置文件不都是XML格式嗎? 我說: (1) Spring IoC的配置文件基本都是屬性設置,Bean ID聲明。沒有邏輯。 (2) 我也不是很贊同Spring IoC在XML配置文件里面引用Java類的做法。這方面,其它的容器如 Pico, Nano都支持多種配置方式,其中包括了不少腳本方式。我覺得,在腳本里面定義生成Java Object,比在XML中要好。當然,Web.xml里面也引用了Java Class名字。但那是非常簡單的情況。沒有嵌套引用、屬性賦值、構造參數等復雜的定義方式。XML適合描述一些通用的資源、數據、結構。比如,HTML, XUL, XAML,RSS就是XML用的恰當的例子。
所以,我的基本觀點是這樣。 (1) 純數據,不用說,應該定義在XML中。 (2) 如果是系統中一些Java Object要用到的基本屬性。比如,連接池大小等。定義在properties, XML, Script中都可以。如果定義中沒有出現具體的Java Class名,傾向于定義在properties, XML文件中。如果出現了具體的Java Class名,傾向于定義在Script中。這個界限不那么明顯,兩者皆可。 (3) 復雜結構的Java Bean的構造生成,那是肯定會出現具體的Java Class名,應該定義在Script中。
關于“可配置 vs 可編程”,有一點要明確:只要是可編程的,一定是可配置的。但如果是可配置的,卻不一定是可編程的。 這里的可編程,是指框架給程序員提供了API;可配置,是指框架給程序員提供了配置文件的格式寫法。 “可編程”一定是“可配置”的。 (1) 用戶至少可以自己定義配置文件,讀取參數,調用API。 (2) 有那么多的解釋腳本可以直接和Java互操作,完全可以直接用來當作配置文件,定義參數。 “可配置” 卻不一定“可編程”的。 如果框架只給你提供了配置方式,而沒有API,那意味著,你只能進行參數的靜態配置。很難在動態期間改變這些參數了。你總不能嘗試著用代碼去改變配置文件的內容吧?即使你改動了,如果框架不進行文件的時間戳檢查,就是一開始裝載進來,就不再檢查更改了,你不就一點辦法都沒有了嗎? 比如,Struts Tiles的XML定義,你只能靜態配置,你想在運行期間改變布局,沒有辦法。Site Mesh也是如此。而我們可以在運行期間任意操作XML DOM Node,別說布局了,任何東西都可以改變。 所以,一個框架首要注重的是提供API,而不是提供配置方式。這是一個重要的原則。
討論完了“可編程”、“可配置”問題,我們來看“熱部署”問題。 XML配置文件、腳本文件支持“熱部署”當然要比編譯語言程序的熱部署容易得多。只要解釋執行前,檢查一下時間戳就可以了。要注意的問題,只是做好測試,因為沒有編譯期的語法檢查。 不過,Java程序也是可以“熱部署”的。只是稍微麻煩一點。典型的例子是JSP, EJB Jar等。JSP修改之后,會自動編譯執行;EJB Jar丟到EJB Container里面,會被檢測到并裝載到JNDI命名空間。 編譯語言Java程序的熱部署的一個可能的技術難點是,Class或者Jar已經存在,如何監測到Class或者Jar的更改,并裝載這個新版本,替換舊版本。 這個問題我具體沒有研究過。從道理上講,應該在Class Loader上下功夫。如果需要,可以參閱開源EJB Container的相關實現部分。Java還有一種“Hot Swap”技術,專門解決這個問題,可以搜索查閱一下。
這段小插曲,就到這里。下面討論Web框架。
6.Web框架 Web框架層是一個清潔的樓層。很多優秀的程序員在這一層大展身手,做出了很多好作品。我感覺不錯的有Spring MVC, Web Work。 對于Web應用來說,Web框架層是最重要的一層。SOA、Semantic Web等效果都要在這一層實現。 首先,我們來討論,框架的編程結構。 我的Blog中有一篇《Java Web框架綜述》的文章。講解了一些流行的Web框架的編程結構,很多重復的內容不再贅述。 http://blog.csdn.net/buaawhl
Java Web框架綜述 http://blog.csdn.net/buaawhl/archive/2004/12/21/224069.aspx
Spring MVC的編程接口是最清晰的。大多數簡單情況下,Web Work的用法是最簡單有效的,編程結構比較特殊,可以說具有一定的變革意義。 Spring MVC的Controller接口相當于Struts Action,也具有Request, Response兩個參數,雖然編程接口非常清晰優雅,但是本質上沒有什么變化。 WebWork的Action則失去了Controller的身份,只相當于FormBean的身份,或者說相當于ActionBean的身份。WebWork Action不具有Request, Response兩個參數,它只具有屬性,并通過屬性Setter獲取HTTP Request的參數,通過屬性getter把結果數據輸出到HTTP Response。 可以說,WebWork的這個把握是相當到位的。95%以上的情況下,程序員是不需要Request, Response參數的。當需要這些參數的時候,WebWork并沒有擋住路,可以通過實現RequestAware,ResponseAware等接口來獲取,或者通過一個Thread Local獲取。這種情況下,編程結構的約定,就不那么清晰了。
我從Canonical的帖子和Blog受到了很多啟發。 http://canonical.blogdriver.com
jsplet:對Model 2模式的批判 http://canonical.blogdriver.com/canonical/591479.html
jsplet與webwork的概念對比 http://canonical.blogdriver.com/canonical/594671.html
從級列理論看MVC架構 http://canonical.blogdriver.com/canonical/579747.html
從Canonical的文章可以看出。JSPLet用JSP文件作為Dispatcher,然后在JSP里面注冊并調用對應的Object。這個尋訪Object的過程,完全是根據豐富的URL定義來做的。URL里面包括Object Scope, Object Name, Method Name, Method Parameters,天生就對事件機制有良好的支持。
Zope的一些做法也有異曲同工之妙。 Zope Object Publishing http://www.zope.org/Documentation/Books/ZDG/current/ObjectPublishing.stx http://www.plope.com/Books/2_7Edition/ZopeArchitecture.stx#2-3
這種通過URL獲取Published Object的服務的思路,是一種實現SOA效果的有效思路。
我們首先來看Web Service的現狀。目前Web Service主要分為兩大陣營。SOAP和REST。關于REST,請參閱 http://www.xfront.com/REST-Web-Services.html 關于SOAP和REST的比較、互操作,網上有很多文章。如果需要請搜索查閱。
我個人比較傾向于REST風格的Web Service。 因為SOAP是一門固定的協議,如果用SOAP來編寫Web Service程序,需要一個SOAP協議的解析庫 ,也許還需要一些專門的“SOAP 數據 -- 編程語言”映射庫,如同CORBA IDL的多語言映射一樣。如果你要讓自己的Web應用支持SOAP,你需要把發布的服務對象、方法都包裝為SOAP協議,這需要一些編程語言相關的數據結構的映射工作。 REST則只是一種風格,而不是一個協議。中心思想是簡單的通過豐富的URI定義 (如XLink + XPointer等) 獲取資源。如果你要讓自己的Web應用支持REST,那么很簡單,只要在URI上下功夫就可以了,比如,多增加一個參數format=REST,在程序中多增加一種XML輸出格式就可以了。(從道理上來說,SOAP也可以這么實現,但SOAP的輸入和輸出都要遵守SOAP協議,SOAP的輸入參數一般都包裝在SOAP信封里面)
關于HTTP Get和Post,我表述一下自己的看法。 我認為,Web的精髓在于Get,而不是Post,在于獲取服務器的輸出,而不是輸入到服務器。即,Web的精髓在于以小搏大,四兩撥千斤。最經典的用法就是用一個URL,獲取一個長篇的文本內容,這個內容里面充滿了其他更多的資源連接。這也是超文本連接HTML發明的初衷。 至于HTTP Post,則是這上面的一個擴展。B/S結構如此流行,很多應用都要轉移到Web上面,怎么辦,應用總是交互的,總要讓用戶輸入數據吧,就增加了HTTP Post協議。 HTTP Get經典、簡單、有效。可以用豐富的URI定義把這個優勢發揮到極致。這個實現也比較簡單、優雅。就不多說了。主要的難點在于HTTP Post。下面的討論主要應對“HTTP Post”這個復雜現象。 HTTP Post從來就不讓人們滿意。當輸入邏輯復雜到一定程度,表單數據的繁雜、凌亂、散落,到了服務器端很難組織起來。輸入方面B/S結構確實和C/S結構難以匹敵。于是,出現了XMLHttp,能夠把參數在瀏覽器里面組織成為一個統一的XML數據結構(或其他格式),發送到服務器端,一次解析出來。SOAP做這個方面,更是拿手好戲。所以,很多XMLHttp程序直接采用SOAP作為通信協議。而REST風格的HTTP Post則和HTML Form Post沒有太大的本質區別。 REST在HTTP Get方面更勝一籌,SOAP在HTTP Post方面更勝一籌。可以根據Web應用的特點,根據HTTP Get / HTTP Post 頁面的比例,選擇適合的技術。 我們再進一步分析HTTP Post的數據內容。HTTP Post的數據,可能包含三種類型: (1) 需要存檔在服務器的數據 比如,用戶注冊時候,輸入的基本信息,用戶名、密碼、電子郵件等。這些信息要存放到服務器的數據庫。 對于這種基本信息,HTTP Post,XMLHttp,SOAP處理起來,難度都不大,沒有很大區別。 B2B的數據交換,也屬于這個類別。用何種技術區別不大。一般采用SOAP,因為SOAP是一種流行的標準協議。 (2) 服務調用參數 比如,用戶進行復合條件查詢的時候,輸入的查詢條件。這個時候,HTTP Post處理起來就非常蹩腳。而XMLHttp,SOAP則具有很大的優勢。可以把復雜的查詢條件很好組織成XML數據,發送到服務器端統一處理。SOAP里面甚至可以定義對象名、方法名等詳細的調用信息。 (3) 指令 這種情況比較少見。上面的參數類別中提到的“對象名、方法名等詳細的調用信息”,和這個指令類別有些交叉。 假如一個SOAP調用方法里面的參數也是一個自定義的對象,這個自定義對象的屬性數據在SOAP信息中進行了定義。到了服務器端之后,服務端程序首先調用這個自定義參數的構造函數,生成這個參數對象,然后調用對應的服務對象,把這個參數傳給服務。這個過程可以看作是一個順序指令:[1]構造參數[2]調用服務。 這只是最簡單的情況。而目前的Web Service一般也就支持到這個程度。 我的看法是,一不做,而不休。既然都把調用信息定義到這個程度了,不如做的更徹底一些,全面完善的支持指令。這個指令則意味著邏輯。前面講過了,我不贊成用XML Tag表示邏輯,而贊成腳本。這里比較適合的腳本是JavaScript,因為JavaScript比較通用,客戶端、服務器端都可以解釋執行。注意,這里和一般的做法正好相反:一般的Web應用總是把JavaScript從服務器傳到瀏覽器里面執行,而這里是把JavaScript在瀏覽器里組織好,發給服務器端處理;這個JavaScript將會在服務器端執行,調用服務器端的對象。舉個SOAP含有JavaScript指令的例子 (只是示意,非標準格式) : <soap envelope> <XML Data> <a> <b>12</b> </a> <c> <d>21</d> </c> <e> <e>16</e> </e> </XML Data>
<script> final_result = default; result1 = service1.service(a.b); if(result1.ok){ result2 = service2.service(c.d); if(result2.ok) final_result = service3.service(e.f); } </script> < /soap envelope >
這個好處是: [1] 發布了更多的基本Service。給客戶提供了更大的靈活度。 比如,這里就發布了3個Service。由用戶自己組織邏輯。 按照傳統的做法,上述流程將整個包裝在服務器端執行。發布給用戶的Service只有最外面的一個Service,而且高度耦合(if, else, if, else流程hard code在服務器端),不靈活,不通用。 這里的方法,就可以讓客戶端隨意組織service1, service2, service3的調用順序和方式。 [2] 減少了通信次數。 假如這段Script在客戶端執行,那么和服務器要進行3次通信。
傳統Web的權限控制一般在URL級別,這種script -> server方式的權限控制則要在對象級別、方法級別、Code片斷級別了,復雜很多,也許要大量應用Java的Code權限認證機制。
以上展開討論了 Web Service, HTTP Get/Post。下面我們回到Web框架層。 前面說了,JSPLet給了我很大的啟發。很多思路可以借鑒。 當然,我并不贊成用JSP作Dispatcher, Controller。(1) 因為JSP要編譯成Servlet,而Servlet是Web Server管理的比較昂貴的資源。一個Web系統中JSP達到幾千個,就會遇到性能瓶頸。(2) JSP中的代碼重用很成問題。一般只能通過include file的方式。 可以借鑒的思路。(1) JSPLet 的入口是JSP文件,這一步的URL到處理程序的映射是Servlet/JSP Container自然支持的。這是免配置的。(2) 豐富的URL參數定義,良好的對象方法尋址能力。
我開發的開源Web框架lightweb,將具備如下特性: (1) 支持兩個層次的編程接口。 interface Action { void service(request, response, servletContext); } 這個Action比Struts Action, Spring MVC Controller高一個級別。相當于Dispatcher, 相當于JSPLet的JSP控制文件。這個用來做最外層的入口控制。 同時,也支持簡單的JavaBean.method的直接調用。相當于WebWork Action,JSPLet Registered Object。這個用來做具體的事情。
(2) 支持豐富的對象尋址URI,比如http://my.com/myProject/myModule/myEntry.action?object=calculator&method=add&p1=1&p2=3 這表示要通過 myEntry.acion這個入口,調用caculator.add(1, 2)方法。 如果用URL Rewriter可以美化為 http://my.com/myProject/myModule/myEntry/calculator/add/1/3 看起來就很象XLink + XPointer了。
(3) 免配置。或者說極少的配置。 框架根據一定的匹配準則,把myModule/myEntry.action映射到 com.mycompany.mymodule.MyEntryAction 這個類的service方法。 這個service方法負責根據object, method的名字,尋找到對應的bean,并根據參數進行屬性設置驗證,并執行對應的bean.method。然后,把這個bean作為Model和template結合,輸出結果。 同樣,template的獲取也是根據一定的匹配準則,根據myModule/myEntry找到 Mymodule/myentry.html 或者Mymodule/myentry/calculator.html。
這樣的lightweb就能夠同時對應簡單和復雜。復雜控制的需求交給Action接口來做,簡單的一般具體任務交給普通Java Bean去做。 Web框架層可以做的非常復雜,可以做的非常簡單。Lightweb的目標,就是分成多個簡單的部分;各部分合起來就能夠完成從非常簡單到非常復雜的需求。 接下來,我們來看O/R。
7.O/R Hibernate, EJB Entity Bean產品,JDO產品,iBatis是比較流行的幾種O/R Mapping Framework。 我做的一些工作中,經常涉及到復雜的優化過的native SQL,并且涉及到大量的批量復雜邏輯處理,現有的O/R框架都不能滿足功能和性能要求。
我做出這樣一個lightor框架,思路借鑒了Martin Fowler的《企業架構模式》里面講述的一些O/R的Row Mapper, Column Mapper等概念。
最經典的用法是: ResultSet rs = ps.executeQuery( a long complex native sql); //will return a lot of records A a = new A(); B b = new B(); IMapper aMapper = MapperService.getMapper(A.class); IMapper bMapper = MapperService.getMapper(B.class);
While(rs.next()){ aMapper.populate(a, rs); bMapper.populate(b, rs);
businessLogic(a, b); }
可以看到,Lightor不需要一下子把所有紀錄都放到一個Object List里面。完全可以隨取隨用。整個過程中,a, b只有一份,極大的節省了空間、時間,也極大的提高了開發效率,減少了重復代碼。 沒有任何一個其它O/R能夠支持這種用法。這里面,lightor的mapper的populate方法需要ResultSet參數。一般的O/R不屑于這么做的,別說ResultSet,連Connection都想包裝起來不給你看。
Lightor的設計思路也是同時應對簡單和復雜。Lightor的Mapper實體部分是自動生成代碼。類似于JDO的靜態Enhance。不同的是,JDO靜態Enhance直接修改bean class。而Lightor則不動原有的bean,只是多生成了對應的Mapper Source/Class。這種方式是最利于跟蹤調試的。至于發布部署,和JDO的情況差不多,不如Hibernate的動態代碼增強。 這里我很羨慕Python, Ruby等動態解釋語言的
這一層我主要關注的是性能,緩存策略等等,而不是簡便。我覺得,一個應用系統的瓶頸主要存在于O/R, DB層。不應該單純為了追求OO結構的優雅,或者編程的方便,而犧牲了一些可能優化的地方。
關于Lightor的緩存策略, 我的Blog上有幾篇文章。 http://blog.csdn.net/buaawhl
數據庫對象的緩存策略 http://blog.csdn.net/buaawhl/archive/2004/12/21/224184.aspx
分頁 & QueryKey & 定長預取 http://blog.csdn.net/buaawhl/archive/2005/01/08/245005.aspx
8.總結 我理想中的Web開發架構是這樣的: 開發速度快,運行速度快,結構清晰優雅。 具體到每一層。 Web框架層主要追求 開發速度快。 O/R層主要追求 運行速度快。 頁面資源層和頁面模板層主要追求 結構清晰優雅。 |