我做Java編程大概有四年了,但我仍然處在從Java初級(jí)程序員向中級(jí)程序員過(guò)渡的階段。 Java 包含的東西實(shí)在太多,三大體系 J2ME,J2SE,J2EE 每個(gè)都包含了大量的內(nèi)容,身為一個(gè) Java 程序員其實(shí)挺悲哀的,不僅要不停地學(xué)習(xí)那么多基礎(chǔ)知識(shí),還要應(yīng)付層出不窮的框架。不光要學(xué)理論,還要注重實(shí)踐。想想原來(lái)搞 Perl, PHP 的時(shí)候,哪來(lái)的那么多知識(shí)點(diǎn)。雖然我的Java水平不高,但我始終對(duì)要學(xué)的東西保持自己的看法,對(duì)于技術(shù)我總是抱著懷疑一切的態(tài)度,有時(shí)候甚至很固執(zhí),就算我是錯(cuò)的,我也要自己撞了南墻來(lái)體驗(yàn)。我的觀點(diǎn)是:"好話左右不了我,我會(huì)自己判斷的。大家都說(shuō)好,我可能偏不喜歡"。
我曾經(jīng)在編程技術(shù)上做過(guò)四次選擇:
1:選擇了 Velocity ,放棄了 JSP.
2: 選擇了 Ruby ,放棄了 Python.
3: 選擇了 Hivemind, 放棄了 Spring.
4: 選擇了 Intellij, 放棄了 Eclipse.
四年前第一次接觸 Java WEB 編程的時(shí)候,我沒(méi)有選擇學(xué)習(xí) JSP ,而是選擇了 Apache 的 Velocity 框架。當(dāng)時(shí)的 freemarker 也是相同類(lèi)型的模板引擎,它甚至可以在模板中使用 JSP 的Taglib,我仍然選擇了 Velocity ,因?yàn)槲矣X(jué)得視圖層并不需要那么復(fù)雜。 Velocity 框架很簡(jiǎn)單,模板語(yǔ)言只負(fù)責(zé)簡(jiǎn)單的顯示邏輯處理,速度更快,沒(méi)有 JSP 第一次編譯的速度延遲,而且它是一個(gè)通用的模板引擎,不光用于 Web 開(kāi)發(fā),如今它還被用在了 Intellij 中做為代碼生成的工具。Freemarker 在模板中使用 Jsp Tag 的功能在我看來(lái)是個(gè)累贅,使得本來(lái)簡(jiǎn)單的東西添加了復(fù)雜性,所以我沒(méi)有選擇它。JSP 從一開(kāi)始設(shè)計(jì)就是錯(cuò)誤的,它沒(méi)有及時(shí)避免出現(xiàn)失控的情形(頁(yè)面中充斥 Java 代碼,這就類(lèi)似于 C++ 沒(méi)有從一開(kāi)始避免程序員寫(xiě) C 那樣的過(guò)程代碼一樣。而且 Taglib 也不是一個(gè)好的擴(kuò)展顯示邏輯的方式。
我在接觸 Java 前曾經(jīng)使用過(guò)一段 Zope 和 Python ,即便是在使用了 Java 兩年后,我仍在研究 Zope 和 Python 。 使用 Python 這樣的腳本語(yǔ)言主要是為了編寫(xiě)一些Java代碼生成工具。如果用 Java 來(lái)寫(xiě)這種工具,無(wú)論編寫(xiě)還是發(fā)布都顯得不那么完善。腳本語(yǔ)言最擅長(zhǎng)是處理文本字符,文件,它們通常內(nèi)置正則表達(dá)式功能,這些都成了編寫(xiě)Code generator 的強(qiáng)有力的工具。 Zope 建立 Web 站點(diǎn)的能力是有目共睹的,相對(duì) Java 來(lái)說(shuō),它的開(kāi)發(fā)效率更好,雖然執(zhí)行效率較低,但是滿足大多數(shù)中小規(guī)模網(wǎng)站的制作。接觸了 Ruby 后,我逐漸離開(kāi)了 Python。 原因是 Ruby 在編程中給我了一種從未有過(guò)的快樂(lè)感。我曾經(jīng)認(rèn)為 Python 的縮進(jìn)語(yǔ)法是它的優(yōu)勢(shì),因?yàn)檫@種強(qiáng)制性使得代碼的可讀性大大的增強(qiáng)了,即便是多年之后再看自己寫(xiě)的代碼,或者是別人寫(xiě)的代碼,你還是很容易讀懂。但是 Python 語(yǔ)言本身的一些弱點(diǎn),使得你感覺(jué)很不舒服,它的面向?qū)ο蟛粡氐?,只是具備一?OO 的特點(diǎn),使得在使用它的時(shí)候有點(diǎn)四不象的感覺(jué)。如果你用過(guò) Ruby 這種感覺(jué)會(huì)更加強(qiáng)烈。不過(guò)這種選擇取決于你是否是一個(gè)“面向?qū)ο缶幊?#8221;的強(qiáng)烈愛(ài)好者,否則 Python 長(zhǎng)期積累的現(xiàn)成的成熟代碼庫(kù)會(huì)使得你很容易的選擇它的。我想對(duì)我個(gè)人編程影響最大的一本書(shū),當(dāng)屬 Dave Thomas 的那本 "Programming Ruby"的電子版了。通過(guò)它我才得以系統(tǒng)地學(xué)習(xí)了 Ruby ,并把我以前用 Python 寫(xiě)的腳本程序全部替換為 Ruby 腳本。直到現(xiàn)在,文件操作和數(shù)據(jù)庫(kù)備份移植的腳本文件我都采用 Ruby ,而這些操作如果用 Java 來(lái)寫(xiě),復(fù)雜性可想而知 。所以我一直認(rèn)為一個(gè)程序員至少應(yīng)該掌握一種腳本語(yǔ)言,并非為了學(xué)習(xí)腳本語(yǔ)言而學(xué)習(xí)它,主要是為了讓自己的編程工作更輕松一點(diǎn)。至于后來(lái)放棄 Zope 而選擇 ROR ,就是再自然不過(guò)的事情了。
Spring 的流行其實(shí)應(yīng)該算是 Java 世界反對(duì) Sun EJB 技術(shù)的一個(gè)必然結(jié)果,就算不是 Rod Johnson 推出 Spring 框架,大家也必然會(huì)采用另外一個(gè)人推出一個(gè)基于 POJO 的 IOC 容器框架的。其實(shí)在那個(gè)時(shí)候推出了很多 IOC 容器框架,比如 Pico Container, 還有那生不逢時(shí)的 Avalon,這些框架都或多或少有些問(wèn)題。Pico 只是個(gè)簡(jiǎn)單的 IOC 容器,功能相對(duì)較弱。Avalon 不僅僅是個(gè) IOC 容器,它更是一個(gè)組件框架。但是它是 type-1 型的框架,也就是說(shuō)它是基于接口的服務(wù),它的缺點(diǎn)是你必須實(shí)現(xiàn)一個(gè)特定的接口,這就造成了對(duì)特定框架的依賴性。早期的容器采用這種方式有其歷史原因,后來(lái)推出的 IOC 容器不是采用 type-2(Setter)設(shè)置屬性的方式來(lái)設(shè)置依賴關(guān)系,就是采用 type-3 (constructor) 構(gòu)造函數(shù)的方式來(lái)設(shè)置依賴關(guān)系。Spring 和 Pico 就是如此,雖然兩者都同時(shí)支持 type-2 和 Type-3 ,但是 Spring 更多使用 Setter 設(shè)置依賴關(guān)系,而 Pico 習(xí)慣采用構(gòu)造函數(shù)。現(xiàn)在很多人都夸大 type-1 型這種接口依賴性的缺點(diǎn),認(rèn)為這是過(guò)時(shí)的框架(事實(shí)上 Avalon 框架最終被 Apache 組織放棄了)。 其實(shí)我并不這么看,我們公司使用 Avalon , Cocoon 框架很多年,對(duì)于一個(gè)多年使用 Avalon 框架的用戶, 對(duì)過(guò)去代碼的依賴性可能使得它不會(huì)簡(jiǎn)單地拋棄它,Avalon 組件式開(kāi)發(fā)方式有其優(yōu)勢(shì),在部署的時(shí)候有極大的好處,這點(diǎn)我在后面談到 Hivemind 的時(shí)候詳細(xì)再說(shuō)。必須繼承某個(gè)框架的特定接口,會(huì)使得將本框架代碼移植到新框架的時(shí)候出現(xiàn)依賴性問(wèn)題,但是如果你不存在更換框架的可能性的話,使用這種 Type-1 型框架就沒(méi)有什么太大的缺點(diǎn)了。人們總是喜歡設(shè)置假想的未來(lái)預(yù)期,比如盡量少用數(shù)據(jù)庫(kù)存儲(chǔ)過(guò)程,因?yàn)檫@樣產(chǎn)生數(shù)據(jù)庫(kù)移植的困難,事實(shí)上真正企業(yè)級(jí)的項(xiàng)目很少會(huì)更換數(shù)據(jù)庫(kù)層的,這點(diǎn)Rod Johnson 在他那本名著《Expert One-on-One J2EE Design And Development 》的第一章就做了闡述。所以對(duì) Type-1 型框架的恐懼也是沒(méi)道理的。 如果你已經(jīng)在使用這樣的框架,那么就堅(jiān)持繼續(xù)使用它,它的成熟性可能會(huì)給你帶來(lái)更大的開(kāi)發(fā)益處,但是如果你是個(gè)新手,那么最好還是采用 2 和 3 型的框架,能避免依賴性的時(shí)候還是應(yīng)該盡量去做。
Spring 現(xiàn)在可以被看作是一個(gè) FullStack 的全功能性框架,它不象 Struts 那樣僅僅是個(gè)簡(jiǎn)單的 MVC 視圖層框架,它包含了對(duì) J2ee 開(kāi)發(fā)各個(gè)方面的支持。在接觸 Spring 框架之前,我就通過(guò)閱讀 Rod Johnson 的 Expert One-on-One J2EE Design And Development 獲益非淺,書(shū)中的各種真知灼見(jiàn)對(duì)任何一個(gè)開(kāi)發(fā)者都有很大的啟發(fā),即便他不采用 Spring 來(lái)開(kāi)發(fā)自己的應(yīng)用?,F(xiàn)在看來(lái) ,Spring 在 Java 語(yǔ)言層次上已經(jīng)做到了盡可能的簡(jiǎn)化和正確的決策。隨著對(duì)它的了解日益加深,我發(fā)現(xiàn)它存在一些問(wèn)題。大家都說(shuō) Spring 的XML配置文件太多了,其實(shí)我并不這么認(rèn)為。看看 Hivemind ,Avalon 這些流行框架,哪個(gè) XML都不少,這是 Java 框架的通病。 Spring 有一個(gè)優(yōu)秀的 IOC 容器,它容納的是 POJO ,但是它并沒(méi)有發(fā)展成為一個(gè)組件框架。Rod Johnson 說(shuō)"面向?qū)ο蟊饶承┘夹g(shù)本身更重要",Spring 也是這么做的,使用它你可以容易的面向接口編程,但是面向?qū)ο缶幊谈嫦蚪M件編程是兩個(gè)階段,面向?qū)ο笾饕窃陂_(kāi)發(fā)階段,而面向組件編程,更關(guān)注于部署 Deploy 階段的工作。 一個(gè)面向組件的框架要考慮更多的動(dòng)態(tài)部署的問(wèn)題。 Spring 的問(wèn)題出在部署上,如果你用過(guò) Avalon 和 Hivemind, 你會(huì)覺(jué)得它們那種通過(guò)替換 Jar 包來(lái)更新功能的方式真的很爽,而這種特點(diǎn)在 Spring 中卻看不到。 在 Spring 編程中,你會(huì)看到清晰的分層:表現(xiàn)層代碼,應(yīng)用層代碼,數(shù)據(jù)持久層代碼,它們各司其職,靠的是XML 配置文件來(lái)描述各層代碼之間的依賴關(guān)系。所有這些代碼和 XML 配置文件是放在一起的,Spring 應(yīng)用的部署就是一種 "All or Nothing "的局面,更新一小段代碼, 你就要更新整個(gè)應(yīng)用的部署.如果采用 Avalon 或 Hivemind 這樣的組件框架, 優(yōu)勢(shì)就明顯了. 使用這樣的組件框架來(lái)開(kāi)發(fā)應(yīng)用, 通常將程序員分類(lèi)兩類(lèi): 組件或服務(wù)開(kāi)發(fā)者(系統(tǒng)程序員), 應(yīng)用程序員. 系統(tǒng)程序員負(fù)責(zé)開(kāi)發(fā)組件或者(服務(wù)), 供應(yīng)用程序員調(diào)用. 舉個(gè)例子, 如果要開(kāi)發(fā)一個(gè)網(wǎng)頁(yè)計(jì)數(shù)器的應(yīng)用, 我可以將計(jì)數(shù)器的功能先定義接口 Counter,然后提供默認(rèn)實(shí)現(xiàn), 如果你采用文件來(lái)記錄計(jì)數(shù)器的數(shù)值,可以提供一個(gè) CouterFileImpl 的實(shí)現(xiàn)類(lèi), 如果采用數(shù)據(jù)庫(kù)來(lái)記錄數(shù)值,那么可以提供一個(gè) CounterDBImpl 的實(shí)現(xiàn)類(lèi). 這種服務(wù)的概念,將原來(lái)的應(yīng)用開(kāi)發(fā)三層的概念(表現(xiàn)層,應(yīng)用層,數(shù)據(jù)持久層)簡(jiǎn)化為兩層(應(yīng)用層,服務(wù)層), 原來(lái)的表現(xiàn)層對(duì)應(yīng)應(yīng)用層,由應(yīng)用程序員開(kāi)發(fā), 而原來(lái)的應(yīng)用層和數(shù)據(jù)持久層對(duì)應(yīng)服務(wù)層,由系統(tǒng)程序員開(kāi)發(fā). 現(xiàn)在流行的各種表現(xiàn)層框架雖然是按照 MVC 模式設(shè)計(jì)的, 但是大多只是實(shí)現(xiàn)了 VC (視圖和控制器),而對(duì)于M(模型)并沒(méi)有提供什么支持. 以前這些框架多是推薦采用 Session Bean 來(lái)實(shí)現(xiàn)模型(M), 但是隨著Spring 等POJO IOC 框架的流行, 模型的實(shí)現(xiàn)可以采用 Spring POJO , 和Avalon, Hivemind 服務(wù)來(lái)替代實(shí)現(xiàn). 應(yīng)用程序員可以采用任何流行的表現(xiàn)層框架來(lái)獲取這些服務(wù)完成顯示的任務(wù). Spring 的問(wèn)題出現(xiàn)在這里: Spring不是組件框架,沿襲原有的三層的概念, 是以 XML 來(lái)組織所有三層代碼. 如果要改任何一層的代碼,就牽扯到整個(gè)代碼同時(shí)更新和部署. 其實(shí)這個(gè)問(wèn)題還是可以通過(guò) Maven 將不同包編譯為 Jar 來(lái)解決, 但這不如組件框架直接提供這樣的服務(wù)好. 如果采用 Hivemind ,可以將服務(wù)接口和對(duì)應(yīng)服務(wù)實(shí)現(xiàn)編譯為 Jar 包, 接口可以定義在一個(gè)Jar 包中, 不同的實(shí)現(xiàn)也可以定義在不同的 Jar 包中, 當(dāng)然接口和所有的實(shí)現(xiàn)也可以都放在一個(gè) Jar 包中. 這樣如果后臺(tái)實(shí)現(xiàn)變化了(文本記錄改為數(shù)據(jù)庫(kù)記錄),那么系統(tǒng)程序員只要提供一個(gè)新的Jar(可以只更新Jar中的配置,修改默認(rèn)實(shí)現(xiàn)的指向)給應(yīng)用程序員, 這個(gè)修改部署就是以Jar包為最小單位.它的簡(jiǎn)單性是 Spring 提供不了的. 如果你看過(guò) Howard Lewis Ship (Tapestry 和 Hivemind 框架的創(chuàng)始人)的日記, 就會(huì)知道他曾經(jīng)在多年前闡述過(guò)這個(gè)問(wèn)題, 并就此跟 Spring 框架的創(chuàng)始人 Rod Johnson 在多個(gè)研討會(huì)上交流過(guò),無(wú)奈 Rod 有自己的觀點(diǎn), 沒(méi)有接受 Howard 的意見(jiàn), 所以才有了后來(lái) Hivemind 框架的問(wèn)世. Spring 鼓勵(lì)程序員采用面向?qū)ο笏枷?面向接口編程, 但是沒(méi)有針對(duì)面向組件提供幫助. 面向組件編程更多的是考慮部署的概念, 如何從當(dāng)前應(yīng)用的類(lèi)路徑中動(dòng)態(tài)找尋一個(gè)類(lèi),類(lèi)路徑下存在一個(gè)類(lèi)的多個(gè)版本,應(yīng)該如何確定加在的版本等等. 擁有組件框架對(duì)于應(yīng)用程序開(kāi)發(fā)者來(lái)說(shuō)有極大的好處, 無(wú)論采用什么表現(xiàn)層框架,它都可以輕易的調(diào)用服務(wù), 對(duì)它來(lái)說(shuō), 后臺(tái)應(yīng)用邏輯的更新, 持久層的更換也只是一個(gè)Jar包替換的問(wèn)題. 如果表現(xiàn)層框架換了, 調(diào)用現(xiàn)有服務(wù)也是很容易的. Spring 基本上是采用XML來(lái)描述各個(gè)POJO 之間的依賴關(guān)系, 通過(guò) Setter 或 Constructor 注入. 我覺(jué)得"在表現(xiàn)層依然采用 IOC 注入方式" 也是一個(gè)問(wèn)題, 如果采用 Hivemind , 使用 Struts 做表現(xiàn)層,你可以通過(guò) Service Locator 的方式獲得服務(wù), 而采用 Tapestry 你也可以采用 IOC 方式注入 Hivemind 服務(wù). 這樣對(duì)應(yīng)用程序開(kāi)發(fā)人員來(lái)說(shuō)就非常靈活了. 所以我推薦大家采用 Hivemind 這種新型的 IOC 組件框架來(lái)開(kāi)發(fā) Java web 應(yīng)用.
我發(fā)現(xiàn)大多數(shù)使用 Java 一兩年的程序員都喜歡構(gòu)造自己的"完美"組合開(kāi)發(fā)框架, 我也是如此. 不過(guò)這方面做的最"過(guò)分"的當(dāng)屬 Matt Raible , 這位仁兄創(chuàng)建了 Appfuse 這個(gè)Starter 框架, 它結(jié)合了當(dāng)今所有流行的框架加以組合來(lái)開(kāi)發(fā)一個(gè)簡(jiǎn)單應(yīng)用. 當(dāng)初只是一個(gè)實(shí)驗(yàn)性的模型, 新的版本已經(jīng)非常復(fù)雜了. Appfuse 也是采用三層組合,每層中都采用最流行的框架, 不過(guò)相對(duì)來(lái)說(shuō), 應(yīng)用層和數(shù)據(jù)持久層采用的框架比較穩(wěn)定,就是 Spring 和 Hibernate , 最近數(shù)據(jù)持久層又添加了 iBatis 和 JDO, 不過(guò)最豐富的當(dāng)屬表現(xiàn)層了, 當(dāng)今主流的 Web 框架,他用了一遍, Webworks, spring mvc, struts, tapestry, jsf . 其實(shí) Matt 現(xiàn)在做的有點(diǎn)過(guò)頭了, 我承認(rèn)我自己也是通過(guò)學(xué)習(xí)他的 Spring Live 一書(shū)來(lái)了解 Spring 開(kāi)發(fā)的. 我對(duì)他那個(gè)相對(duì)簡(jiǎn)單的 equinox (Appfuse Light 版本)框架更感興趣一點(diǎn), 現(xiàn)在的 Appfuse 對(duì)我來(lái)說(shuō),已經(jīng)膨脹到無(wú)法看下去的地步了. 其實(shí)他如此組合沒(méi)有什么太大的意義,最終程序員都會(huì)有自己的"完美"方案的. 我的選擇是 Spring MVC + Hivemind + Hibernate . 以前中間應(yīng)用層我是采用 Spring , 因?yàn)?Spring 和 Hibernate 的結(jié)合比較完美, Template 的確簡(jiǎn)化了代碼量,使用也很簡(jiǎn)單. 但是隨著 Hibernate 3.x 的推出, 原有的 Checked Exception 都改為 Unchecked Exception 了, 使用 Spring 的優(yōu)勢(shì)就不那么明顯了, 而組件框架部署的優(yōu)勢(shì)就成了首先考慮的問(wèn)題. Spring MVC 是 Spring 框架中一個(gè)從頭開(kāi)發(fā)的子框架, 它的設(shè)計(jì)比較完善,功能很多: 多視圖層輸出, 向?qū)ы?yè)面等等, 如果你采用Flash 來(lái)制作 RIA 應(yīng)用就可以采用 Velocity做視圖層生成 XML 數(shù)據(jù), 也可以利用 Spring 的 Webservice 集成功能. 這是我心目中一個(gè)完美的組合.
現(xiàn)在還有不少人正在繼續(xù)構(gòu)造自己“完美”的框架,其實(shí)這種追求完美的過(guò)程是永無(wú)止境的,正如那個(gè)廣告中說(shuō)的“沒(méi)有最好,只有更好”。記得以前看《六人行》第一季中有這么一集,Chandler 公司的一個(gè)女同事說(shuō)要給他介紹一個(gè)“完美”的男友,Chandler 說(shuō)了這么一句話 “Ah, y'see, perfect might be a problem. Had you said 'co-dependent', or 'self-destructive'”,當(dāng)時(shí)我并沒(méi)有理解這句話的意思,現(xiàn)在覺(jué)得這似乎是描述這種追求完美框架行為最合適的一句話了。我想,可能有不少Java程序員跟我一樣,打算尋找一個(gè)最完美的組合框架來(lái)做一個(gè)自己的應(yīng)用,結(jié)果這個(gè)應(yīng)用過(guò)了一兩年都沒(méi)完成。Java 程序員過(guò)于重視理論上完美的設(shè)計(jì),而忽略實(shí)際的效果。看看如此眾多的 Java 框架竟然構(gòu)造不出一個(gè)好的論壇程序,所有出色的論壇都是出自 PHP 和 PERL,的確很 讓 Java 程序員羞愧的?,F(xiàn)在看來(lái),真正的 Pragmatic Programmer( 實(shí)用主義程序員)都來(lái)自這些腳本語(yǔ)言的使用者,他們沒(méi)那么多思想包袱,是真正篤信“實(shí)踐出真知”的一群人, Java 社區(qū)應(yīng)該多向他們學(xué)習(xí)。對(duì)于 Java 程序員來(lái)說(shuō),關(guān)鍵不在于你是否尋找到一個(gè)完美的框架組合,而是要做出一個(gè)好的應(yīng)用。在論壇與人空談,與人論戰(zhàn)是沒(méi)有任何意義的, David Heinemeier Hansson 建立 Rails 是在做項(xiàng)目的過(guò)程中產(chǎn)生的,他沒(méi)有采用任何已有的框架,也沒(méi)有去和別人辯論,而是靠實(shí)事說(shuō)話把整個(gè) Java 業(yè)界攪的天翻地覆。 我想這點(diǎn)值得廣大 Java 程序員深思。