cuiyi's blog(崔毅 crazycy)

          記錄點(diǎn)滴 鑒往事之得失 以資于發(fā)展
          數(shù)據(jù)加載中……

          Java Web開(kāi)發(fā)構(gòu)想 (很好的文章,轉(zhuǎn))

          Java Web開(kāi)發(fā)構(gòu)想
          作者:buaawhl

          1.背景、形勢(shì)
          能夠進(jìn)行Web開(kāi)發(fā)的編程語(yǔ)言和技術(shù)很多
          (1) 動(dòng)態(tài)解釋語(yǔ)言
          PHP; Perl; Python (Zope, Plone); Ruby (Ruby on Rails);
          (2) 編譯語(yǔ)言
          Java; .net

          Java Web開(kāi)發(fā)遠(yuǎn)非一枝獨(dú)秀:
          除了受到來(lái)自.net 這個(gè)重量級(jí)對(duì)手的最大挑戰(zhàn)之外,更受到Zope, Ruby on Rail 等新式輕騎兵的沖擊(當(dāng)然,也繼續(xù)受到老式輕步兵PHP, Perl的沖擊)。

          官方Java走的是復(fù)雜路線(xiàn),Servlet -> JSP -> Taglib。.net走的也是復(fù)雜路線(xiàn),依靠成熟友好的集成化開(kāi)發(fā)環(huán)境取勝。Java陣營(yíng)好容易應(yīng)對(duì)過(guò)來(lái),從紛紜復(fù)雜的各種開(kāi)發(fā)框架基礎(chǔ)上,發(fā)展出了重量級(jí)Web開(kāi)發(fā)框架JSF,以及相應(yīng)的集成化開(kāi)發(fā)環(huán)境;渴望以此應(yīng)對(duì).net的攻勢(shì)。勝負(fù)未分,前途未卜。這時(shí),另一個(gè)方向又殺來(lái)了新式輕騎Zope, Ruby on Rail。
          Python, Ruby等動(dòng)態(tài)解釋語(yǔ)言,面向?qū)ο筇匦愿?,先天支?動(dòng)態(tài)綁定、AOP、函數(shù)式編程、“編程即配置”等時(shí)髦概念。開(kāi)發(fā)速度更快,代碼量更小,達(dá)到killer級(jí)別。

          傳統(tǒng)的HTML Web開(kāi)發(fā)領(lǐng)域里面,Java已經(jīng)是腹背受敵。領(lǐng)域外也展開(kāi)了征戰(zhàn),Rich Client Architecture的興起:AJAX(XMLHttp), Flash RIA, XUL, XAML, Smart Client(以及從前的ActiveX, Applet, Web Start)。

          Web的發(fā)展趨勢(shì)是 語(yǔ)義Web,最終目的是讓整個(gè)Web成為一個(gè)巨大的數(shù)據(jù)庫(kù)。
          這意味著,未來(lái)的Web應(yīng)用將更加的面向文本內(nèi)容數(shù)據(jù),更加搜索引擎友好 – Search Engine Friendly.
          二進(jìn)制的客戶(hù)端插件,如Flash RIA, ActiveX, Applet, Web Start等,雖然交互性能最好,但不是以文本內(nèi)容數(shù)據(jù)為中心,搜索引擎不友好。所以,我只是保持適當(dāng)關(guān)注。我更關(guān)注基于文本的UI表現(xiàn),如HTML, XUL, XAML等。XUL, XAML還沒(méi)有廣泛流行,只是保持一種有興趣的關(guān)注。
          當(dāng)下關(guān)注的重點(diǎn),還是 XHTML + CSS + Javascript少量的 AJAX(XMLHttp)增加更好的交互性。

          我一直認(rèn)為:輕量、簡(jiǎn)潔、高效 才是硬道理。后面闡述我對(duì)Java Web開(kāi)發(fā)的理解和構(gòu)想。

          2. Web開(kāi)發(fā)框架層次概述
          從上到下,Web開(kāi)發(fā)框架的層次如下:
          (1) HTML, JavaScript, CSS等頁(yè)面資源。
          (2) 頁(yè)面模板層。
          如JSP, Freemarker, Velocity, XSL,fastm等。用來(lái)生成HTML, JavaScript, CSS等頁(yè)面資源。
          (3) Web框架。把HTTP Request調(diào)度分派到對(duì)應(yīng)的Service Entry。
          (4) Business Logic.
          (5) O/R Mapping.
          (6) JDBC
          (7) DB

          根據(jù)我的經(jīng)驗(yàn),一個(gè)典型的Web應(yīng)用中的代碼比例如下:
          頁(yè)面邏輯約占 50%,商業(yè)邏輯約占30%, O/R 約占20%。

          但事實(shí)上,頁(yè)面卻是最不受重視的部分,從來(lái)都被認(rèn)為是臟活,累活,雜活。典型的開(kāi)發(fā)過(guò)程通常是這樣:
          頁(yè)面設(shè)計(jì)人員迅速的用Dreamweaver等生成一堆文本雜亂無(wú)章的頁(yè)面,然后交給JSP程序員加入更加雜亂無(wú)章的Java代碼和Taglib。
          當(dāng)頁(yè)面布局風(fēng)格需要改變的時(shí)候,頁(yè)面設(shè)計(jì)人員用Dreamweaver等生成一堆新的頁(yè)面。JSP程序員再重新加入更加雜亂無(wú)章的Java代碼Taglib。
          至于頁(yè)面中的腳本邏輯調(diào)試,更是一門(mén)精深的工夫了。

          根據(jù)社會(huì)規(guī)則,通常來(lái)說(shuō),工作內(nèi)容越輕松,收入越高;工作內(nèi)容越臟月累,收入越低;Web開(kāi)發(fā)也是如此:做著最臟最累的活的頁(yè)面程序員,工資一般比不上后臺(tái)業(yè)務(wù)邏輯程序員。

          開(kāi)發(fā)框架通常會(huì)帶來(lái)這樣的結(jié)果:讓簡(jiǎn)單的東西,變得更簡(jiǎn)單;讓復(fù)雜的東西,變得更復(fù)雜。
          這其中的原因在于:
          一般來(lái)說(shuō),一個(gè)應(yīng)用中簡(jiǎn)單重復(fù)的東西占80%,復(fù)雜特殊的東西占20%。
          簡(jiǎn)單重復(fù)的東西很容易摸清規(guī)律,進(jìn)行包裝,通用化。但是,在包裝的同時(shí),經(jīng)常就阻擋住了底層的一些靈活強(qiáng)大的控制能力。在復(fù)雜特殊的需求中,確實(shí)又需要這些底層控制能力,那么為了繞開(kāi)框架的限制,付出的努力要比不用框架 大得多。
          打個(gè)比方,一個(gè)比較極端的例子。編譯語(yǔ)言比匯編語(yǔ)言的開(kāi)發(fā)效率高很多,但是卻無(wú)法直接操作寄存器。當(dāng)需要在編譯語(yǔ)言中操作寄存器的時(shí)候,就非常的痛苦。比如Java,也許需要JNI,寫(xiě)C代碼,還要在C代碼里面嵌入?yún)R編。編譯、連接都很麻煩。
          所以,一個(gè)框架的開(kāi)發(fā)效率,就在于這個(gè)80%簡(jiǎn)單 與 20%復(fù)雜之間的平衡。
          假如,不用框架來(lái)開(kāi)發(fā),簡(jiǎn)單的80%要消耗 80個(gè)資源數(shù),復(fù)雜的20%要消耗20個(gè)資源數(shù),總資源數(shù)是100;使用了某個(gè)框架,簡(jiǎn)單的80%只要消耗10個(gè)資源數(shù),復(fù)雜的20%要消耗40個(gè)資源數(shù),總資源數(shù)是50。那么,我們說(shuō),這個(gè)開(kāi)發(fā)框架是有效率的。

          我的思路是,同時(shí)應(yīng)對(duì)復(fù)雜和簡(jiǎn)單。當(dāng)然,為了應(yīng)對(duì)復(fù)雜,簡(jiǎn)單的東西可能就應(yīng)對(duì)得不那么好。比如,做這樣一個(gè)開(kāi)發(fā)框架,簡(jiǎn)單的80%要消耗20個(gè)資源數(shù),復(fù)雜的20%要消耗10個(gè)資源數(shù),總資源數(shù)是30。
          這種開(kāi)發(fā)框架是有可能實(shí)現(xiàn)的。而且是很有意義的。尤其是在復(fù)雜部分的比例提高的時(shí)候。越復(fù)雜的系統(tǒng),這種開(kāi)發(fā)框架就越有意義。

          后面的關(guān)于Web各層開(kāi)發(fā)的論述,主要就按照這個(gè)“應(yīng)對(duì)復(fù)雜、讓復(fù)雜更簡(jiǎn)單”的思路展開(kāi)。

          3.頁(yè)面資源
          也許有人會(huì)說(shuō),頁(yè)面資源,不就是HTML嗎?太簡(jiǎn)單,太低極了,沒(méi)勁。Dreamweaver、Frontpage多簡(jiǎn)單阿。隨便找個(gè)人來(lái)用就可以了。文本內(nèi)容亂糟糟不要緊,瀏覽器里面顯示出來(lái)的效果好看就行。要增加炫的、酷的動(dòng)畫(huà)效果,那就寫(xiě)JavaScript唄。寫(xiě)在HTML里面,看看在IE里面能不能運(yùn)行就可以了唄。
          這也正是大多數(shù)公司開(kāi)發(fā)頁(yè)面資源的方式。因?yàn)轫?yè)面的需求變化是最多、最快的,而頁(yè)面的制作成本很低,人們不愿意在上面投入更多的資源。

          我的看法是,萬(wàn)丈高樓平地起。應(yīng)用程序的每一個(gè)部分都應(yīng)該完善管理,結(jié)構(gòu)優(yōu)美。越是需求變化多的地方,越是臟亂差的地方,越應(yīng)該加大力度處理好。

          頁(yè)面結(jié)構(gòu)方面,Javaeye論壇的Dlee做了很多工作。

          (1) 在 2005 年我們?nèi)绾螌?xiě) JavaScript
          http://forum.javaeye.com/viewtopic.php?t=12973
          (2)使用 Unordered Lists 制作的下拉菜單和樹(shù)
          http://forum.javaeye.com/viewtopic.php?t=12995
          從上面的Dlee的論述和給出的資料??梢钥闯觯?yè)面資源分為三部分:
          (1) XHTML。結(jié)構(gòu),Structure。
          XHTML里面的Tag部分只應(yīng)該包括 <ul> <table> <p> <div><span>等結(jié)構(gòu)布局Tag,或者<strong><emphasis>表示語(yǔ)義的Tag。
          XHTML里面不應(yīng)該包括風(fēng)格信息,比如字體、顏色、大小、粗細(xì)等,也不應(yīng)該包括<font> <b> <i> <h> 等字體信息。
          XHTML里面不應(yīng)該包括Javascript的定義和調(diào)用。

          (2) JavaScript。行為,behavior。
          JavaScritp應(yīng)該存在于一個(gè)獨(dú)立于XHTML文件的獨(dú)立文件中。這樣可以做自動(dòng)化單元測(cè)試。JavaScript應(yīng)該只改變HTML DOM的結(jié)構(gòu)和內(nèi)容,而不應(yīng)該改變它的風(fēng)格。

          (3) CSS。Style,風(fēng)格?;蛘哒f(shuō),Presentation,表現(xiàn)。
          前面說(shuō)了,XHTML里面不應(yīng)該包括JavaScript的調(diào)用。那么,XHTML的元素是如何JavaScript事件綁定起來(lái)?就是在CSS里面指定的。
          當(dāng)然,眾所周知,CSS的本職工作是處理頁(yè)面風(fēng)格。

          頁(yè)面資源方面,我完全認(rèn)同Dlee的觀點(diǎn)。從技術(shù)和資源積累的長(zhǎng)遠(yuǎn)目標(biāo)看來(lái),這方面的初期投入的回報(bào)將是非常豐厚的。
          即使將來(lái)HTML消亡了,進(jìn)入了XAML, XUL, RSS時(shí)代,這些結(jié)構(gòu)清晰的各部分,重用的可能性都非常巨大。JavaScript + CSS + XML UI的這種經(jīng)典設(shè)計(jì)思路,將留存很久?;祀s成一團(tuán)的HTML的命運(yùn)只能是全盤(pán)被拋棄。

          4.頁(yè)面模板層
          頁(yè)面模板層是指Server端運(yùn)行的用來(lái)生成HTML(或JavaScript,CSS)的Server Side Template Engine。
          這一層也是著名的臟亂差樓層。著名的HTML的Java代碼污染事件,就發(fā)生在這個(gè)樓層。不僅JSP有這個(gè)問(wèn)題,其他的template, 如freemarker, velocity, tapestry等含有邏輯的腳本,都不同程度上有HTML的Script Logic污染問(wèn)題。

          Dlee的做法很美。直接就不要頁(yè)面模板層,不用Server Side Template Engine。直接用JavaScript更改HTML DOM的結(jié)構(gòu)、內(nèi)容、數(shù)據(jù)。同時(shí),會(huì)用到少量的瀏覽器端XSL。
          這樣帶來(lái)的結(jié)果,Template就是很干凈純粹的HTML,不含有任何Server Side Script。這個(gè)效果,和Servier Side Template 的 Jivan,XMLC達(dá)到的一樣。只是一個(gè)是在瀏覽器端執(zhí)行,一個(gè)是在Server端執(zhí)行。

          我研究比較了幾乎所有的Server Side Template Engine,力圖采眾家之長(zhǎng),避眾家之短,寫(xiě)了一個(gè)Server Side Template Engine -- fastm, 能夠最優(yōu)雅方便的實(shí)現(xiàn)頁(yè)面模板層。關(guān)于fastm,我的Blog上有不少文章論述。
          我的Blog,里面專(zhuān)門(mén)有個(gè)fastm 分類(lèi)。
          http://blog.csdn.net/buaawhl
          http://buaawhl.blogdriver.com/

          Fastm發(fā)布在java.net上。
          https://fastm.dev.java.net/


          我仍然對(duì)Server Side Template Engine持肯定態(tài)度?;谌缦略颍?
          (1) JavaScript代碼量大、文件多的時(shí)候,不容易管理,不容易進(jìn)行語(yǔ)法檢查,不容易跟蹤調(diào)試。
          這里有人會(huì)爭(zhēng)辯,Server Side Template Engine也用到了很多腳本阿,比如Freemarker, Velocity, 而且嵌在HTML中,怎么管理,怎么調(diào)試?即使是JSP,也是Java Code嵌在HTML里面,怎么管理,怎么調(diào)試?
          這里我要說(shuō),Jivan, XMLC, fastm,Wicket等Template Engine的邏輯都是在Java Code里面。

          (2) 用JavaScript生成文本內(nèi)容,搜索引擎不友好。
          一般的網(wǎng)絡(luò)蜘蛛程序,只根據(jù)URL獲取HTML文本,搜索里面的文本內(nèi)容,而不會(huì)執(zhí)行里面的JavaScript腳本。

          (3) JavaScript代碼重用還是有些局限
          比如,有兩個(gè)HTML文件,一個(gè)是Table布局,一個(gè)是List布局。
          我有同樣的一批數(shù)據(jù),要在這兩種布局中顯示。
          這時(shí)候,就要給這兩個(gè)HTML分別寫(xiě)兩套JavaScript。這里面的DOM層次,元素,屬性都不同,再怎么定義ID,Class,也無(wú)法用完全相同的一套JavaScript處理。
          這里有人會(huì)爭(zhēng)辯,Server Side Template Engine也無(wú)法做到。別說(shuō)JSP, Velocity, Freemarker等要在兩套HTML里面嵌入相同的代碼,就是Jivan, XMLC, Wicket也要分別寫(xiě)不同的兩套Java Code,因?yàn)樗鼈兊腦ML DOM Node / Model View (Table, List) 都是不同的。
          這里我要說(shuō)。fastm可以做到只用一套代碼邏輯。而且只有fastm可以。fastm的代碼重用率是最高的。

          關(guān)于Ajax(XMLHttp),我的意見(jiàn)是必要時(shí)才用,而且最好采用粗粒度的用法 -- JavaScript發(fā)出一個(gè)URL請(qǐng)求,返回一整段HTML,直接替換到頁(yè)面的某一塊,而不是用JavaScript來(lái)做這樣的把數(shù)據(jù)填充到HTML DOM中。如果你直接在瀏覽器里面輸入那個(gè)URL,也可以獲取那整段的HTML內(nèi)容。
          典型的應(yīng)用場(chǎng)合是Portal。Portal頁(yè)面的每個(gè)Portlet都含有這樣的 Ajax(XMLHttp) javascript代碼 -- 發(fā)出一個(gè)Portlet URL請(qǐng)求,返回一整段Portlet的內(nèi)容,直接替換當(dāng)前的Portlet塊。
          這樣做的好處是:
          (1) 減少JavaScript代碼的量和復(fù)雜度。
          (2) 搜索引擎友好。網(wǎng)絡(luò)蜘蛛程序可以辨別JavaScript中的URL,并根據(jù)這個(gè)URL,獲取整段處理好的HTML文本,進(jìn)行內(nèi)容搜索。
          有人可能會(huì)爭(zhēng)辯:如果URL請(qǐng)求返回的是XML數(shù)據(jù),不是整段處理好的HTML,搜索引擎也可以進(jìn)行內(nèi)容搜索。
          這點(diǎn)我同意。前提是XML數(shù)據(jù)的內(nèi)容是足夠連貫的,而不是散落的。比如,你返回的XML數(shù)據(jù)是“中國(guó)”。這個(gè)“中國(guó)”要放在HTML中的一個(gè){country}位置,{country}足球。這個(gè)時(shí)候,結(jié)果HTML的內(nèi)容含有“中國(guó)足球”。而XML數(shù)據(jù)中只含有“中國(guó)”。如果用戶(hù)用“中國(guó)足球”作為關(guān)鍵字來(lái)搜索,就找不到這個(gè)URL。

          從前面給出的fastm資料的連接中,可以得知。如同Jivan, XMLC, Wicket一樣,fastm的template里面不含有邏輯,所有的邏輯都寫(xiě)在Java里面。
          有人會(huì)爭(zhēng)辯說(shuō):頁(yè)面邏輯寫(xiě)在Java里面,我改變了頁(yè)面邏輯,還需要重新編譯。這也太不方便了。Velocity, Freemarker, JSP就不用重新編譯。
          這里我的看法是:業(yè)務(wù)邏輯代碼改變了,不也需要重新編譯嗎?頁(yè)面邏輯就不是邏輯了嗎?HTML里面的腳本怎么語(yǔ)法檢查、跟蹤調(diào)試?業(yè)務(wù)邏輯需要語(yǔ)法檢查、跟蹤調(diào)試,頁(yè)面邏輯就不需要語(yǔ)法檢查、跟蹤調(diào)試了嗎?
          對(duì)方可能會(huì)說(shuō):在我的應(yīng)用中,頁(yè)面邏輯的改動(dòng)需求非常頻繁,而且這些頁(yè)面邏輯非常簡(jiǎn)單,不需要語(yǔ)法檢查、跟蹤調(diào)試。
          這里我的意見(jiàn)是:
          (1) 那就使用JSP, Velocity, Freemarker等腳本。
          (2) fastm, Jivan, XMLC, Wicket的Java代碼部分也可以寫(xiě)在腳本里面,比如,Server Side JavaScript, Jython(Python), Groovy, Bean Shell 等腳本語(yǔ)言都可以很方便的和Java相互調(diào)用。

          fastm的生命周期將很長(zhǎng)。
          HTML, XUL, XAML都是,或?qū)⑹强梢栽跒g覽器或可視化編輯工具里面顯示的XML UI定義語(yǔ)言。Microsoft Office的Word, Excel, Powerpoint等格式都提供了相應(yīng)的XML格式。這些XML文件都可以在Office里面顯示,并編輯。
          Adobe公司也提供了PDF的XML格式 -- XDP。可以在Adobe Designer里面顯示并編輯。
          由于fastm是Designer Friendly的XML UI所見(jiàn)即所得的模板技術(shù)。這方面具有很大的潛力。
          根本不需要第三方花大力氣專(zhuān)門(mén)做個(gè)IDE,來(lái)顯示自定義的Tag。目標(biāo)文件格式提供商自己的閱讀編輯工具就可以直接用了,而且效果就是運(yùn)行后產(chǎn)生的結(jié)果文件的效果。

          即使沒(méi)有可視化要求的場(chǎng)合。比如,Web Service需要的XML數(shù)據(jù)。fastm同樣有用武之地。比如,
          <!-- BEGIN DYNAMIC: users -->
          <user>
          <name>{name}</name>
          <address>{name}</address>
          </user>
          <!-- END DYNAMIC: users -->

          可以很容易的把一個(gè)Java Object List轉(zhuǎn)化為XML數(shù)據(jù)。

          另外,我不得不承認(rèn)。瀏覽器端的JavaScript的頁(yè)面邏輯,可移植性要高于Server Side Template Engine。因?yàn)镾erver Side Template Engine通常是特定語(yǔ)言相關(guān)的。
          目前fastm是用Java實(shí)現(xiàn)的。由于實(shí)現(xiàn)很簡(jiǎn)單,移植到其它的語(yǔ)言,也很簡(jiǎn)單。如果是移植到Python, Ruby等動(dòng)態(tài)解釋語(yǔ)言,那就更簡(jiǎn)單了。我是有這個(gè)考慮,因?yàn)閆ope, Ruby on Rails 的模板還是Logic 和 HTML混雜的,fastm這個(gè)思路有很大的用武之地。

          前面講了這么多。清理了兩層有名的臟亂差的老大難的樓層 -- 頁(yè)面資源層和頁(yè)面模板層。讓這兩層變得和下面的樓層同樣的優(yōu)雅、清潔。

          下面該講到Web框架層了。在向下講之前,由于前面提到了腳本,我想先插入一段關(guān)于“可配置”、“可編程”、“可熱部署”、“腳本邏輯 vs XML Tag邏輯”的話(huà)題。把這個(gè)人們比較關(guān)心、討論比較多的話(huà)題,先講清楚。

          5.可配置、可編程、可熱部署、腳本邏輯 vs XML Tag邏輯
          由于Java是編譯語(yǔ)言,人們通常把變化的參數(shù)部分抽取出來(lái),放到配置文件中。
          這些配置文件通常是XML文件。這很好,沒(méi)什么問(wèn)題。XML很適合用來(lái)表達(dá)數(shù)據(jù)結(jié)構(gòu)。
          但是,對(duì)于某一種技術(shù)的狂熱,通常引起對(duì)這種技術(shù)的過(guò)度使用,或者誤用。
          人們開(kāi)始覺(jué)得,XML能夠表達(dá)一切東西,包括for, if, else等邏輯。這方面的典型例子有 Workflow XML Definition,Logic TagLib, XSL Logic Tag等。
          這點(diǎn)我不敢茍同。我的看法是,XML不適合表達(dá)邏輯,XML表達(dá)邏輯非常蹩腳。XML表達(dá)邏輯相當(dāng)于自定義一門(mén)XML格式的腳本語(yǔ)言。

          比如,Logic Tablib,很難自然的支持 if else, switch。只能蹩腳地支持一堆 <logic:if> <logic:ifNot> <logic:exists> <logic:notExists> <logic:ifNull> <logic:notNull>。
          (注,好久沒(méi)有接觸過(guò)Taglib了。這些Tag Name都是憑以前的使用印象寫(xiě)的,也許名字不對(duì),但表達(dá)這些意思的TagLib都還是有的)
          如果要表達(dá)if () else if() else 就更蹩腳了。要進(jìn)行非常麻煩的嵌套。

          再比如,XSL 支持if, else 也非常蹩腳。非要多出來(lái)一個(gè)層次才行。
          <xsl:choose>
          <xsl:when test="…">
          …. If ….
          </xsl:when>
          <xsl:otherwise>
          … else …
          </xsl:otherwise>
          </xsl:choose>

          同樣,如果要表達(dá)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 表達(dá)邏輯,非常麻煩,可讀性很差,完全是一種誤用,沒(méi)有半點(diǎn)優(yōu)勢(shì)。當(dāng)然,邏輯簡(jiǎn)單的情況下,還是可以接受的。
          有人會(huì)說(shuō):XML表達(dá)邏輯,可以免編譯阿。
          那么我說(shuō):語(yǔ)法檢查呢,跟蹤調(diào)試呢?
          對(duì)方說(shuō):只是一些簡(jiǎn)單的邏輯,不需要語(yǔ)法檢查、跟蹤調(diào)試。
          我說(shuō):如果只是為了免編譯,前面列出的那么多的解釋執(zhí)行的腳本語(yǔ)言更適合。XML表達(dá)的邏輯,比Java等編譯語(yǔ)言還要麻煩很多,而腳本語(yǔ)言比Java等編譯語(yǔ)言簡(jiǎn)潔多了,可讀性非常好,而且腳本語(yǔ)言和Java語(yǔ)言有很好的交互性,可以相互調(diào)用。重用、結(jié)構(gòu)方面都具有優(yōu)勢(shì)。

          有人會(huì)舉出Spring IoC為例子,說(shuō):你看,Spring IoC的配置文件不都是XML格式嗎?
          我說(shuō):
          (1) Spring IoC的配置文件基本都是屬性設(shè)置,Bean ID聲明。沒(méi)有邏輯。
          (2) 我也不是很贊同Spring IoC在XML配置文件里面引用Java類(lèi)的做法。這方面,其它的容器如 Pico, Nano都支持多種配置方式,其中包括了不少腳本方式。我覺(jué)得,在腳本里面定義生成Java Object,比在XML中要好。當(dāng)然,Web.xml里面也引用了Java Class名字。但那是非常簡(jiǎn)單的情況。沒(méi)有嵌套引用、屬性賦值、構(gòu)造參數(shù)等復(fù)雜的定義方式。XML適合描述一些通用的資源、數(shù)據(jù)、結(jié)構(gòu)。比如,HTML, XUL, XAML,RSS就是XML用的恰當(dāng)?shù)睦印?

          所以,我的基本觀點(diǎn)是這樣。
          (1) 純數(shù)據(jù),不用說(shuō),應(yīng)該定義在XML中。
          (2) 如果是系統(tǒng)中一些Java Object要用到的基本屬性。比如,連接池大小等。定義在properties, XML, Script中都可以。如果定義中沒(méi)有出現(xiàn)具體的Java Class名,傾向于定義在properties, XML文件中。如果出現(xiàn)了具體的Java Class名,傾向于定義在Script中。這個(gè)界限不那么明顯,兩者皆可。
          (3) 復(fù)雜結(jié)構(gòu)的Java Bean的構(gòu)造生成,那是肯定會(huì)出現(xiàn)具體的Java Class名,應(yīng)該定義在Script中。

          關(guān)于“可配置 vs 可編程”,有一點(diǎn)要明確:只要是可編程的,一定是可配置的。但如果是可配置的,卻不一定是可編程的。
          這里的可編程,是指框架給程序員提供了API;可配置,是指框架給程序員提供了配置文件的格式寫(xiě)法。
          “可編程”一定是“可配置”的。
          (1) 用戶(hù)至少可以自己定義配置文件,讀取參數(shù),調(diào)用API。
          (2) 有那么多的解釋腳本可以直接和Java互操作,完全可以直接用來(lái)當(dāng)作配置文件,定義參數(shù)。
          “可配置” 卻不一定“可編程”的。
          如果框架只給你提供了配置方式,而沒(méi)有API,那意味著,你只能進(jìn)行參數(shù)的靜態(tài)配置。很難在動(dòng)態(tài)期間改變這些參數(shù)了。你總不能?chē)L試著用代碼去改變配置文件的內(nèi)容吧?即使你改動(dòng)了,如果框架不進(jìn)行文件的時(shí)間戳檢查,就是一開(kāi)始裝載進(jìn)來(lái),就不再檢查更改了,你不就一點(diǎn)辦法都沒(méi)有了嗎?
          比如,Struts Tiles的XML定義,你只能靜態(tài)配置,你想在運(yùn)行期間改變布局,沒(méi)有辦法。Site Mesh也是如此。而我們可以在運(yùn)行期間任意操作XML DOM Node,別說(shuō)布局了,任何東西都可以改變。
          所以,一個(gè)框架首要注重的是提供API,而不是提供配置方式。這是一個(gè)重要的原則。

          討論完了“可編程”、“可配置”問(wèn)題,我們來(lái)看“熱部署”問(wèn)題。
          XML配置文件、腳本文件支持“熱部署”當(dāng)然要比編譯語(yǔ)言程序的熱部署容易得多。只要解釋執(zhí)行前,檢查一下時(shí)間戳就可以了。要注意的問(wèn)題,只是做好測(cè)試,因?yàn)闆](méi)有編譯期的語(yǔ)法檢查。
          不過(guò),Java程序也是可以“熱部署”的。只是稍微麻煩一點(diǎn)。典型的例子是JSP, EJB Jar等。JSP修改之后,會(huì)自動(dòng)編譯執(zhí)行;EJB Jar丟到EJB Container里面,會(huì)被檢測(cè)到并裝載到JNDI命名空間。
          編譯語(yǔ)言Java程序的熱部署的一個(gè)可能的技術(shù)難點(diǎn)是,Class或者Jar已經(jīng)存在,如何監(jiān)測(cè)到Class或者Jar的更改,并裝載這個(gè)新版本,替換舊版本。
          這個(gè)問(wèn)題我具體沒(méi)有研究過(guò)。從道理上講,應(yīng)該在Class Loader上下功夫。如果需要,可以參閱開(kāi)源EJB Container的相關(guān)實(shí)現(xiàn)部分。Java還有一種“Hot Swap”技術(shù),專(zhuān)門(mén)解決這個(gè)問(wèn)題,可以搜索查閱一下。

          這段小插曲,就到這里。下面討論Web框架。

          6.Web框架
          Web框架層是一個(gè)清潔的樓層。很多優(yōu)秀的程序員在這一層大展身手,做出了很多好作品。我感覺(jué)不錯(cuò)的有Spring MVC, Web Work。
          對(duì)于Web應(yīng)用來(lái)說(shuō),Web框架層是最重要的一層。SOA、Semantic Web等效果都要在這一層實(shí)現(xiàn)。
          首先,我們來(lái)討論,框架的編程結(jié)構(gòu)。
          我的Blog中有一篇《Java Web框架綜述》的文章。講解了一些流行的Web框架的編程結(jié)構(gòu),很多重復(fù)的內(nèi)容不再贅述。
          http://blog.csdn.net/buaawhl

          Java Web框架綜述
          http://blog.csdn.net/buaawhl/archive/2004/12/21/224069.aspx

          Spring MVC的編程接口是最清晰的。大多數(shù)簡(jiǎn)單情況下,Web Work的用法是最簡(jiǎn)單有效的,編程結(jié)構(gòu)比較特殊,可以說(shuō)具有一定的變革意義。
          Spring MVC的Controller接口相當(dāng)于Struts Action,也具有Request, Response兩個(gè)參數(shù),雖然編程接口非常清晰優(yōu)雅,但是本質(zhì)上沒(méi)有什么變化。
          WebWork的Action則失去了Controller的身份,只相當(dāng)于FormBean的身份,或者說(shuō)相當(dāng)于ActionBean的身份。WebWork Action不具有Request, Response兩個(gè)參數(shù),它只具有屬性,并通過(guò)屬性Setter獲取HTTP Request的參數(shù),通過(guò)屬性getter把結(jié)果數(shù)據(jù)輸出到HTTP Response。
          可以說(shuō),WebWork的這個(gè)把握是相當(dāng)?shù)轿坏摹?5%以上的情況下,程序員是不需要Request, Response參數(shù)的。當(dāng)需要這些參數(shù)的時(shí)候,WebWork并沒(méi)有擋住路,可以通過(guò)實(shí)現(xiàn)RequestAware,ResponseAware等接口來(lái)獲取,或者通過(guò)一個(gè)Thread Local獲取。這種情況下,編程結(jié)構(gòu)的約定,就不那么清晰了。

          我從Canonical的帖子和Blog受到了很多啟發(fā)。
          http://canonical.blogdriver.com/

          jsplet:對(duì)Model 2模式的批判
          http://canonical.blogdriver.com/canonical/591479.html

          jsplet與webwork的概念對(duì)比
          http://canonical.blogdriver.com/canonical/594671.html

          從級(jí)列理論看MVC架構(gòu)
          http://canonical.blogdriver.com/canonical/579747.html

          從Canonical的文章可以看出。JSPLet用JSP文件作為Dispatcher,然后在JSP里面注冊(cè)并調(diào)用對(duì)應(yīng)的Object。這個(gè)尋訪(fǎng)Object的過(guò)程,完全是根據(jù)豐富的URL定義來(lái)做的。URL里面包括Object Scope, Object Name, Method Name, Method Parameters,天生就對(duì)事件機(jī)制有良好的支持。

          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

          這種通過(guò)URL獲取Published Object的服務(wù)的思路,是一種實(shí)現(xiàn)SOA效果的有效思路。

          我們首先來(lái)看Web Service的現(xiàn)狀。目前Web Service主要分為兩大陣營(yíng)。SOAP和REST。關(guān)于REST,請(qǐng)參閱
          http://www.xfront.com/REST-Web-Services.html
          關(guān)于SOAP和REST的比較、互操作,網(wǎng)上有很多文章。如果需要請(qǐng)搜索查閱。

          我個(gè)人比較傾向于REST風(fēng)格的Web Service。
          因?yàn)镾OAP是一門(mén)固定的協(xié)議,如果用SOAP來(lái)編寫(xiě)Web Service程序,需要一個(gè)SOAP協(xié)議的解析庫(kù) ,也許還需要一些專(zhuān)門(mén)的“SOAP 數(shù)據(jù) -- 編程語(yǔ)言”映射庫(kù),如同CORBA IDL的多語(yǔ)言映射一樣。如果你要讓自己的Web應(yīng)用支持SOAP,你需要把發(fā)布的服務(wù)對(duì)象、方法都包裝為SOAP協(xié)議,這需要一些編程語(yǔ)言相關(guān)的數(shù)據(jù)結(jié)構(gòu)的映射工作。
          REST則只是一種風(fēng)格,而不是一個(gè)協(xié)議。中心思想是簡(jiǎn)單的通過(guò)豐富的URI定義 (如XLink + XPointer等) 獲取資源。如果你要讓自己的Web應(yīng)用支持REST,那么很簡(jiǎn)單,只要在URI上下功夫就可以了,比如,多增加一個(gè)參數(shù)format=REST,在程序中多增加一種XML輸出格式就可以了。(從道理上來(lái)說(shuō),SOAP也可以這么實(shí)現(xiàn),但SOAP的輸入和輸出都要遵守SOAP協(xié)議,SOAP的輸入?yún)?shù)一般都包裝在SOAP信封里面)

          關(guān)于HTTP Get和Post,我表述一下自己的看法。
          我認(rèn)為,Web的精髓在于Get,而不是Post,在于獲取服務(wù)器的輸出,而不是輸入到服務(wù)器。即,Web的精髓在于以小搏大,四兩撥千斤。最經(jīng)典的用法就是用一個(gè)URL,獲取一個(gè)長(zhǎng)篇的文本內(nèi)容,這個(gè)內(nèi)容里面充滿(mǎn)了其他更多的資源連接。這也是超文本連接HTML發(fā)明的初衷。
          至于HTTP Post,則是這上面的一個(gè)擴(kuò)展。B/S結(jié)構(gòu)如此流行,很多應(yīng)用都要轉(zhuǎn)移到Web上面,怎么辦,應(yīng)用總是交互的,總要讓用戶(hù)輸入數(shù)據(jù)吧,就增加了HTTP Post協(xié)議。
          HTTP Get經(jīng)典、簡(jiǎn)單、有效??梢杂秘S富的URI定義把這個(gè)優(yōu)勢(shì)發(fā)揮到極致。這個(gè)實(shí)現(xiàn)也比較簡(jiǎn)單、優(yōu)雅。就不多說(shuō)了。主要的難點(diǎn)在于HTTP Post。下面的討論主要應(yīng)對(duì)“HTTP Post”這個(gè)復(fù)雜現(xiàn)象。
          HTTP Post從來(lái)就不讓人們滿(mǎn)意。當(dāng)輸入邏輯復(fù)雜到一定程度,表單數(shù)據(jù)的繁雜、凌亂、散落,到了服務(wù)器端很難組織起來(lái)。輸入方面B/S結(jié)構(gòu)確實(shí)和C/S結(jié)構(gòu)難以匹敵。于是,出現(xiàn)了XMLHttp,能夠把參數(shù)在瀏覽器里面組織成為一個(gè)統(tǒng)一的XML數(shù)據(jù)結(jié)構(gòu)(或其他格式),發(fā)送到服務(wù)器端,一次解析出來(lái)。SOAP做這個(gè)方面,更是拿手好戲。所以,很多XMLHttp程序直接采用SOAP作為通信協(xié)議。而REST風(fēng)格的HTTP Post則和HTML Form Post沒(méi)有太大的本質(zhì)區(qū)別。
          REST在HTTP Get方面更勝一籌,SOAP在HTTP Post方面更勝一籌。可以根據(jù)Web應(yīng)用的特點(diǎn),根據(jù)HTTP Get / HTTP Post 頁(yè)面的比例,選擇適合的技術(shù)。
          我們?cè)龠M(jìn)一步分析HTTP Post的數(shù)據(jù)內(nèi)容。HTTP Post的數(shù)據(jù),可能包含三種類(lèi)型:
          (1) 需要存檔在服務(wù)器的數(shù)據(jù)
          比如,用戶(hù)注冊(cè)時(shí)候,輸入的基本信息,用戶(hù)名、密碼、電子郵件等。這些信息要存放到服務(wù)器的數(shù)據(jù)庫(kù)。
          對(duì)于這種基本信息,HTTP Post,XMLHttp,SOAP處理起來(lái),難度都不大,沒(méi)有很大區(qū)別。
          B2B的數(shù)據(jù)交換,也屬于這個(gè)類(lèi)別。用何種技術(shù)區(qū)別不大。一般采用SOAP,因?yàn)镾OAP是一種流行的標(biāo)準(zhǔn)協(xié)議。
          (2) 服務(wù)調(diào)用參數(shù)
          比如,用戶(hù)進(jìn)行復(fù)合條件查詢(xún)的時(shí)候,輸入的查詢(xún)條件。這個(gè)時(shí)候,HTTP Post處理起來(lái)就非常蹩腳。而XMLHttp,SOAP則具有很大的優(yōu)勢(shì)。可以把復(fù)雜的查詢(xún)條件很好組織成XML數(shù)據(jù),發(fā)送到服務(wù)器端統(tǒng)一處理。SOAP里面甚至可以定義對(duì)象名、方法名等詳細(xì)的調(diào)用信息。
          (3) 指令
          這種情況比較少見(jiàn)。上面的參數(shù)類(lèi)別中提到的“對(duì)象名、方法名等詳細(xì)的調(diào)用信息”,和這個(gè)指令類(lèi)別有些交叉。
          假如一個(gè)SOAP調(diào)用方法里面的參數(shù)也是一個(gè)自定義的對(duì)象,這個(gè)自定義對(duì)象的屬性數(shù)據(jù)在SOAP信息中進(jìn)行了定義。到了服務(wù)器端之后,服務(wù)端程序首先調(diào)用這個(gè)自定義參數(shù)的構(gòu)造函數(shù),生成這個(gè)參數(shù)對(duì)象,然后調(diào)用對(duì)應(yīng)的服務(wù)對(duì)象,把這個(gè)參數(shù)傳給服務(wù)。這個(gè)過(guò)程可以看作是一個(gè)順序指令:[1]構(gòu)造參數(shù)[2]調(diào)用服務(wù)。
          這只是最簡(jiǎn)單的情況。而目前的Web Service一般也就支持到這個(gè)程度。
          我的看法是,一不做,而不休。既然都把調(diào)用信息定義到這個(gè)程度了,不如做的更徹底一些,全面完善的支持指令。這個(gè)指令則意味著邏輯。前面講過(guò)了,我不贊成用XML Tag表示邏輯,而贊成腳本。這里比較適合的腳本是JavaScript,因?yàn)镴avaScript比較通用,客戶(hù)端、服務(wù)器端都可以解釋執(zhí)行。注意,這里和一般的做法正好相反:一般的Web應(yīng)用總是把JavaScript從服務(wù)器傳到瀏覽器里面執(zhí)行,而這里是把JavaScript在瀏覽器里組織好,發(fā)給服務(wù)器端處理;這個(gè)JavaScript將會(huì)在服務(wù)器端執(zhí)行,調(diào)用服務(wù)器端的對(duì)象。舉個(gè)SOAP含有JavaScript指令的例子 (只是示意,非標(biāo)準(zhǔn)格式) :
          <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 >

          這個(gè)好處是:
          [1] 發(fā)布了更多的基本Service。給客戶(hù)提供了更大的靈活度。
          比如,這里就發(fā)布了3個(gè)Service。由用戶(hù)自己組織邏輯。
          按照傳統(tǒng)的做法,上述流程將整個(gè)包裝在服務(wù)器端執(zhí)行。發(fā)布給用戶(hù)的Service只有最外面的一個(gè)Service,而且高度耦合(if, else, if, else流程hard code在服務(wù)器端),不靈活,不通用。
          這里的方法,就可以讓客戶(hù)端隨意組織service1, service2, service3的調(diào)用順序和方式。
          [2] 減少了通信次數(shù)。
          假如這段Script在客戶(hù)端執(zhí)行,那么和服務(wù)器要進(jìn)行3次通信。

          傳統(tǒng)Web的權(quán)限控制一般在URL級(jí)別,這種script -> server方式的權(quán)限控制則要在對(duì)象級(jí)別、方法級(jí)別、Code片斷級(jí)別了,復(fù)雜很多,也許要大量應(yīng)用Java的Code權(quán)限認(rèn)證機(jī)制。

          以上展開(kāi)討論了 Web Service, HTTP Get/Post。下面我們回到Web框架層。
          前面說(shuō)了,JSPLet給了我很大的啟發(fā)。很多思路可以借鑒。
          當(dāng)然,我并不贊成用JSP作Dispatcher, Controller。(1) 因?yàn)镴SP要編譯成Servlet,而Servlet是Web Server管理的比較昂貴的資源。一個(gè)Web系統(tǒng)中JSP達(dá)到幾千個(gè),就會(huì)遇到性能瓶頸。(2) JSP中的代碼重用很成問(wèn)題。一般只能通過(guò)include file的方式。
          可以借鑒的思路。(1) JSPLet 的入口是JSP文件,這一步的URL到處理程序的映射是Servlet/JSP Container自然支持的。這是免配置的。(2) 豐富的URL參數(shù)定義,良好的對(duì)象方法尋址能力。

          我開(kāi)發(fā)的開(kāi)源Web框架lightweb,將具備如下特性:
          (1) 支持兩個(gè)層次的編程接口。
          interface Action { void service(request, response, servletContext); }
          這個(gè)Action比Struts Action, Spring MVC Controller高一個(gè)級(jí)別。相當(dāng)于Dispatcher, 相當(dāng)于JSPLet的JSP控制文件。這個(gè)用來(lái)做最外層的入口控制。
          同時(shí),也支持簡(jiǎn)單的JavaBean.method的直接調(diào)用。相當(dāng)于WebWork Action,JSPLet Registered Object。這個(gè)用來(lái)做具體的事情。

          (2) 支持豐富的對(duì)象尋址URI,比如http://my.com/myProject/myModule/myEntry.action?object=calculator&method=add&p1=1&p2=3
          這表示要通過(guò) myEntry.acion這個(gè)入口,調(diào)用caculator.add(1, 2)方法。
          如果用URL Rewriter可以美化為
          http://my.com/myProject/myModule/myEntry/calculator/add/1/3
          看起來(lái)就很象XLink + XPointer了。

          (3) 免配置。或者說(shuō)極少的配置。
          框架根據(jù)一定的匹配準(zhǔn)則,把myModule/myEntry.action映射到
          com.mycompany.mymodule.MyEntryAction 這個(gè)類(lèi)的service方法。
          這個(gè)service方法負(fù)責(zé)根據(jù)object, method的名字,尋找到對(duì)應(yīng)的bean,并根據(jù)參數(shù)進(jìn)行屬性設(shè)置驗(yàn)證,并執(zhí)行對(duì)應(yīng)的bean.method。然后,把這個(gè)bean作為Model和template結(jié)合,輸出結(jié)果。
          同樣,template的獲取也是根據(jù)一定的匹配準(zhǔn)則,根據(jù)myModule/myEntry找到
          Mymodule/myentry.html 或者M(jìn)ymodule/myentry/calculator.html。

          這樣的lightweb就能夠同時(shí)對(duì)應(yīng)簡(jiǎn)單和復(fù)雜。復(fù)雜控制的需求交給Action接口來(lái)做,簡(jiǎn)單的一般具體任務(wù)交給普通Java Bean去做。
          Web框架層可以做的非常復(fù)雜,可以做的非常簡(jiǎn)單。Lightweb的目標(biāo),就是分成多個(gè)簡(jiǎn)單的部分;各部分合起來(lái)就能夠完成從非常簡(jiǎn)單到非常復(fù)雜的需求。
          接下來(lái),我們來(lái)看O/R。

          7.O/R
          Hibernate, EJB Entity Bean產(chǎn)品,JDO產(chǎn)品,iBatis是比較流行的幾種O/R Mapping Framework。
          我做的一些工作中,經(jīng)常涉及到復(fù)雜的優(yōu)化過(guò)的native SQL,并且涉及到大量的批量復(fù)雜邏輯處理,現(xiàn)有的O/R框架都不能滿(mǎn)足功能和性能要求。

          我做出這樣一個(gè)lightor框架,思路借鑒了Martin Fowler的《企業(yè)架構(gòu)模式》里面講述的一些O/R的Row Mapper, Column Mapper等概念。

          最經(jīng)典的用法是:
          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不需要一下子把所有紀(jì)錄都放到一個(gè)Object List里面。完全可以隨取隨用。整個(gè)過(guò)程中,a, b只有一份,極大的節(jié)省了空間、時(shí)間,也極大的提高了開(kāi)發(fā)效率,減少了重復(fù)代碼。
          沒(méi)有任何一個(gè)其它O/R能夠支持這種用法。這里面,lightor的mapper的populate方法需要ResultSet參數(shù)。一般的O/R不屑于這么做的,別說(shuō)ResultSet,連Connection都想包裝起來(lái)不給你看。

          Lightor的設(shè)計(jì)思路也是同時(shí)應(yīng)對(duì)簡(jiǎn)單和復(fù)雜。Lightor的Mapper實(shí)體部分是自動(dòng)生成代碼。類(lèi)似于JDO的靜態(tài)Enhance。不同的是,JDO靜態(tài)Enhance直接修改bean class。而Lightor則不動(dòng)原有的bean,只是多生成了對(duì)應(yīng)的Mapper Source/Class。這種方式是最利于跟蹤調(diào)試的。至于發(fā)布部署,和JDO的情況差不多,不如Hibernate的動(dòng)態(tài)代碼增強(qiáng)。
          這里我很羨慕Python, Ruby等動(dòng)態(tài)解釋語(yǔ)言的

          這一層我主要關(guān)注的是性能,緩存策略等等,而不是簡(jiǎn)便。我覺(jué)得,一個(gè)應(yīng)用系統(tǒng)的瓶頸主要存在于O/R, DB層。不應(yīng)該單純?yōu)榱俗非驩O結(jié)構(gòu)的優(yōu)雅,或者編程的方便,而犧牲了一些可能優(yōu)化的地方。

          關(guān)于Lightor的緩存策略, 我的Blog上有幾篇文章。
          http://blog.csdn.net/buaawhl

          數(shù)據(jù)庫(kù)對(duì)象的緩存策略
          http://blog.csdn.net/buaawhl/archive/2004/12/21/224184.aspx

          分頁(yè) & QueryKey & 定長(zhǎng)預(yù)取
          http://blog.csdn.net/buaawhl/archive/2005/01/08/245005.aspx

          8.總結(jié)
          我理想中的Web開(kāi)發(fā)架構(gòu)是這樣的:
          開(kāi)發(fā)速度快,運(yùn)行速度快,結(jié)構(gòu)清晰優(yōu)雅。
          具體到每一層。
          Web框架層主要追求 開(kāi)發(fā)速度快。
          O/R層主要追求 運(yùn)行速度快。
          頁(yè)面資源層和頁(yè)面模板層主要追求 結(jié)構(gòu)清晰優(yōu)雅。

          posted on 2007-05-01 22:54 crazycy 閱讀(2215) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): JavaEE技術(shù)

          評(píng)論

          # re: Java Web開(kāi)發(fā)構(gòu)想 (很好的文章,轉(zhuǎn))  回復(fù)  更多評(píng)論   

          說(shuō),永遠(yuǎn)比作容易。。

          但是會(huì)做的, 永遠(yuǎn)比不上會(huì)說(shuō)的。。。
          2007-05-02 14:39 | ddd
          主站蜘蛛池模板: 澄江县| 淮安市| 万源市| 阿合奇县| 太仆寺旗| 伊宁县| 蒲江县| 云龙县| 太和县| 微博| 海晏县| 松原市| 保亭| 和静县| 陆川县| 鄯善县| 平定县| 浦北县| 阿克苏市| 昌江| 天等县| 稻城县| 双柏县| 静宁县| 景宁| 新河县| 张北县| 逊克县| 长岭县| 营口市| 台北市| 北京市| 文安县| 张掖市| 临武县| 崇礼县| 永德县| 金门县| 青海省| 秭归县| 墨玉县|