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