談笑有鴻儒,往來無白丁

          在恰當的時間、地點以恰當的方式表達給恰當的人...  閱讀的時候請注意分類,佛曰我日里面是談笑文章,其他是各個分類的文章,積極的熱情投入到寫博的隊伍中來,支持blogjava做大做強!向dudu站長致敬>> > 我的微博敬請收聽
          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做了很多工作。
          我再說一下把頁面的 structure、presentation 和 behavior 分離開的意義。一般的人很容易理解把 structure 和 presentation 分離的意義,但是對于為什么需要把 presentation 和 behavior 分離開不是很清楚。
          這三部分分離開,頁面開發才有可能實現真正的重用,從而最終降低開發和維護的工作量。JS 代碼是可以做自動測試的,使用 JsUnit 來做。Web 表示層代碼測試困難是公認的,直到今天所有介紹 TDD 的經典教材也沒有提出一個好方法。所以我問過一些朋友,都是傾向于不對表示層的代碼做自動測試。為什么不做自動測試?沒有重用價值的代碼值得做自動測試嗎?而且我們以前有個誤區,認為如果做自動測試,表示層的所有東西都需要測試,其實這個想法是錯誤的。我們需要做自動測試的僅僅是 behavior 這一部分。以前的測試為什么很困難?就是因為 presentation 和 behavior 沒有分離開,我們在 JS 代碼中直接操作頁面的樣式(直接設置元素的 style)。我們不應該再這樣做下去,我們應該把 presentation 的工作完全交給 CSS 來做。實現 presentation 和 behavior 的分離有兩種方法,通過改變元素的 id 或者使用更通用的方法,通過改變元素的 className。而關于這個 id 或者這個 className 具體的樣式在外部的 CSS 文件中設置。JS 文件可以生成新的 structure(createElement,etc.),但是不應該直接改變元素的 style。改變了 style,一切效果你都需要用眼睛看到了才算測試成功,這哪里可以做自動測試?而且假如用戶對這個 style 不滿意,你還需要去修改 JS 代碼。你如果只改變元素的 id 或者 className,做自動測試就要容易得多,你只需要測試最終這個元素的 id 或者 className 是否變成了期望的值。而最終的樣式是不是也是你期望的,那是 CSS 文件保證的事情,這只需要在第一次開發 CSS 的時候做一下人工測試就足夠了。而這樣以來,CSS 文件可以由美工來維護,他完全不需要知道 JS 是什么東西。界面程序員可以去做一些更加重要的事情。

          所以在這里我們看到,把 presentation 和 behavior 徹底分離開是做 Web 表示層代碼自動測試的關鍵。把這兩部分分離開以后,自動測試的難題就迎刃而解了。再強調一下,只有 behavior 有可能做自動測試,presentation 是不需要也不大可能做自動測試的。

          相關資料:
          http://www.onlinetools.org/articles/unobtrusivejavascript/cssjsseparation.html



          從上面的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層主要追求 運行速度快。
          頁面資源層和頁面模板層主要追求 結構清晰優雅。
          posted on 2006-11-08 17:00 壞男孩 閱讀(497) 評論(0)  編輯  收藏 所屬分類: java命令學習
          主站蜘蛛池模板: 三原县| 通辽市| 城口县| 肥城市| 晋州市| 嘉荫县| 南乐县| 烟台市| 滁州市| 射洪县| 芦山县| 汶上县| 页游| 肃北| 华容县| 房产| 淮滨县| 静宁县| 黄梅县| 陕西省| 北票市| 平塘县| 广平县| 无棣县| 江津市| 府谷县| 固原市| 镶黄旗| 广平县| 凉城县| 南涧| 永年县| 贡山| 铜梁县| 扶沟县| 三门县| 寻乌县| 集贤县| 霍林郭勒市| 永兴县| 乐清市|