IBM WebSphere 開發者技術期刊: 最重要的 Java EE 最佳實踐

引言

在過去的幾乎整整十年中,人們編寫了很多有關 Java™ Platform, Enterprise Edition (Java EE) 最佳實踐的內容。現在有十多本書籍和數以百計(可能更多)的文章,提供了關於應該 如何編寫 Java EE 應用程序的見解。事實上,這方面的參考資料如此之多,並且這些參考資料之 間往往還存在着一些矛盾的建議,以至於在這些混雜的內容中進行學習本身也成爲了採用 Java EE 的障礙。因此,爲了給剛進入這個領域的客戶提供一些簡單的指導,我們彙編了這個最重要的最佳 實踐列表,其中包括我們認爲最重要和最有效的 Java EE 最佳實踐。遺憾的是,我們無法僅在 10 大最佳實踐中描述所有需要介紹的內容。因此,爲了避免遺漏關鍵的最佳實踐和尊重 Java EE 的 發展,我們的列表中包含了“19 大”關鍵的 Java EE 最佳實踐。



 


最重要的最佳實踐

 

  1. 始終使用 MVC 框架。
  2. 不要做重複的工作。
  3. 在每一層都應用自動單元測試和測試管理。
  4. 按照規範來進行開發,而不是按照應用服務器來進行開發。
  5. 從一開始就計劃使用 Java EE 安全性。
  6. 創建您所知道的。
  7. 當使用 EJB 組件時,始終使用會話 Facade。
  8. 使用無狀態會話 Bean,而不是有狀態會話 Bean。
  9. 使用容器管理的事務。
  10. 將 JSP 作爲表示層的首選。
  11. 當使用 HttpSession 時,儘量只將當前事務所需要的狀態保存其中,其他內容 不要保存在 HttpSession 中。
  12. 充分利用應用服務器中不需要修改代碼的特性。
  13. 充分利用現有的環境。
  14. 充分利用應用服務器環境所提供的服務質量。
  15. 充分利用 Java EE,不要欺騙。
  16. 安排進行版本更新。
  17. 在代碼中所有關鍵的地方,使用標準的日誌框架記錄程序的狀態。
  18. 在完成相應的任務後,請始終進行清理。
  19. 在開發和測試過程中遵循嚴格的程序。

 





1. 始終使用 MVC 框架。

將業務邏輯(Java Bean 和 EJB 組件)從控制器邏輯(Servlet/Struts 操作)和表示邏輯(JSP、XML/XSLT)中清晰 地分離出來。良好的分層可以帶來許多好處。


這項實踐非常重要,以致沒有其他最佳實踐可以與其相提並論。對於良好的 Java EE 應 用程序設計而言,模型-視圖-控制器 (MVC) 是至關重要的。它將程序的任務簡單地分爲下面幾個 部分:

  1. 負責業務邏輯的部分(模型,通常使用 Enterprise JavaBeans™ 或傳統 Java 對 象來實現)。
  2. 負責用戶接口表示的部分(視圖)。
  3. 負責應用程序導航的部分(控制器,通常使用 Java Servlet 或類 Struts 控制器這樣相 關的類來實現)。

 

對於 Java EE,有許多關於這個主題的優秀評論,我們特別推薦感興趣的讀者可以參考 [Fowler] 或者 [Brown](請參見參考資料部分)的評論,以便全面和深入地瞭解相關內容。

如果不遵循基本的 MVC 體系結構,在開發過程中就會出現許多的問題。最常見的問題是,將過 多的任務放到該體系結構的視圖部分中。可能存在使用 JSP 標記來執行數據庫訪問,或者在 JSP 中進行應用程序的流程控制,這在小規模的應用程序中是比較常見的,但是,隨着後期的開發,這 樣做將會帶來問題,因爲 JSP 逐步變得越來越難以維護和調試。

類似地,我們也經常看到將視圖層構建到業務邏輯的情況。例如,一個常見的問題就是將在構 建視圖時使用的 XML 解析技術直接應用到業務層。業務層應該對業務對象進行操作,而不是對與 視圖相關的特定數據表示進行操作。

然而,僅僅使用適當的組件無法實現應用程序的正確分層。我們常常見到一些應用程序包含 Servlet、JSP 和 EJB 組件所有這三項,然而,其主要的業務邏輯卻是在 Servlet 層實現的,或 者應用程序導航是在 JSP 中處理的。您必須對代碼進行嚴格的檢查和重構,以確保僅在模型層中 處理業務邏輯,在控制器層中進行應用程序導航,而視圖應該只關心如何將模型對象呈現爲合適的 HTML 和 Javascript™。

本文中這項建議的涵義應該比原始版本中的更加清楚。用戶接口技術不斷地發生着變化,將業 務邏輯關聯於用戶接口,會使得對接口的更改影響到現有的系統。幾年之前,Web 應用程序用戶接 口開發人員可能從 Servlet 和 JSP、Struts 和 XML/XSL 轉換中進行選擇。在那以後,Tiles 和 Faces 非常流行,而現在,AJAX 大行其道。如果每當首選的用戶接口技術發生了更改就要重新開 發應用程序的核心業務邏輯,那麼就糟透了。





回頁首


2. 不要做重複的工作。

使用常見的、經過證 實的框架,如 Apache Struts、JavaServer Faces 和 Eclipse RCP。使用經過證實的模式。


回到我們開始幫助客戶使用剛出現的 Java EE 標準的時候,我們發現(和許多其他人一 樣),通過直接使用基礎的 Servlet 和 JSP 規範構建 UI 應用程序來開發用戶接口開發框架,可 以極大地提高開發人員工作效率。因此,許多公司開發了他們自己的 UI 框架,這些框架可以簡化 接口開發的任務。

隨着開放源碼的框架(如 Apache Struts)的出現 [Brown],我們相信,可以自動地和快速地轉換到這些新的框架。我 們認爲,使用開放源碼社區支持的框架非常適合於開發人員,並且這些框架很快得到了廣泛認可, 不僅可用於新的開發,還可以修改現有的應用程序。

但令人感到奇怪的是,事實並非如此。我們仍可以看到許多公司在維護或甚至開發新的用戶接 口框架,而這些框架的功能與 Struts 或者 JSF 是完全相同的。之所以會出現這種情況,有許多 原因:機構惰性,“非我發明”症,不瞭解更改現有代碼的好處、或者甚至傲慢地認爲 能夠比開放源碼開發人員的特定框架做得更好。

然而,這些原因都已經過時了,不能夠成爲不採用標準框架的藉口。Struts 和 JSF 不僅在 Java 社區中得到了廣泛認可,而且還受到 WebSphere 運行時和 Rational® 工具套件的全面 支持。同樣地,在富客戶端領域中,Eclipse RCP(富客戶端平臺,Rich Client Platform)獲得 了廣泛的認可,可用於構建獨立的富客戶端。儘管不是 Java EE 標準中的一部分,但這些框架現 在已成爲 Java EE 社區的一部分,並且理應如此。

對於那些因爲傲慢而不願使用現成的 UI 框架的人,應該閱讀 [Alur] 和 [Fowler] 中介紹的內容。這兩本書詳細地描述了企業 Java 應用程序中最常用的可重用模式。從類似於會話 Facade 這樣簡單的模式(將在後面的建議 中討論)到類似於 Fowler 持久性模式(許多開放源碼的持久性框架對其進行了實現)這樣比較復 雜的模式,其中的內容體現了 Java 前輩們所積累的智慧。那些不能吸取教訓的人必定會重蹈覆轍 (如果他們非常幸運,能夠在第一次失敗之後獲得重來一次的機會),他們不得不向哲學家 Santayana 說抱歉。





回頁首


3. 在應用程序的每一層都使用自動單元測試和測試管 理。

不要只是測試您的圖 形用戶界面(GUI)。分層的測試使得調試和維護工作變得極其簡單。


在過去的幾年中,在方法學領域有了相當大的革新,例如新出現的被稱爲 Agile(如參考資料部分中的 SCRUM [Schwaber] 和極限編程 [Beck1])的輕 量級方法現在已經得到了很普遍的應用。幾乎所有的這些方法中的一個共同特徵是它們都提倡使用 自動的測試工具,這些工具可以幫助開發人員用更少的時間進行迴歸測試,並可以幫助他們避免由 於不充分的迴歸測試造成的錯誤,因此可以用來提高程序員的工作效率。實際上,還有一種被稱爲 Test-First Development [Beck2] 的方法,這種方法甚至提倡在開發實際的代碼之前就先編 寫單元測試。然而,在您測試代碼之前,您需要將代碼分割成一些可測試的片斷。一個“大 泥球”是難以測試的,因爲它不是隻實現一個簡單的易於識別的功能。如果您的每個代碼片 斷實現多個方面的功能,將難以測試其中的每個部分以保證其正確性。

MVC 體系結構(以及 Java EE 中的 MVC 實現)的一個優點就是元素的組件化能夠(實際上, 相當的簡單)對您的應用程序進行單元測試。因此,您可以方便地對實體 Bean、會話 Bean 以及 JSP 獨立編寫測試用例,而不必考慮其他代碼。現在有許多用於 Java EE 測試的框架和工具,這 些框架及工具使得這一過程更加簡單。例如,JUnit(是一種由 junit.org 開發的開 放源代碼工具)和 Cactus(由 Apache 協會開發的開放源代碼工具)對於測試 Java EE 組件都非 常有用。[Hightower] 詳細探討了如何在 Java EE 中使用這些工具。

儘管所有這些詳述了怎樣徹底地測試您的應用程序,但是我們仍然看到一些人認爲只要他們測 試了 GUI(可能是基於 Web 的 GUI,或者是獨立的 Java 應用程序),則他們就全面地測試了整 個應用程序。僅進行 GUI 測試是不夠的。GUI 測試很難達到全面的測試,有以下幾種原因。

  1. 使用 GUI 測試很難徹底地測試到系統的每一條路徑,GUI 僅僅是影響系統的一種方式。 可能存在後臺運算、腳本和各種各樣的其他訪問點,這也需要進行測試,然而,它們通常並不具有 GUI。
  2. GUI 級的測試是一種非常粗粒度的測試。這種測試只是在宏觀水平上測試系統的行爲,這 意味着一旦發現存在問題,則與此問題相關的整個子系統都要進行檢查,這使得找出錯誤將是非常 困難的事情。
  3. GUI 測試通常只有在整個開發週期的後期才能很好地得到測試,這是因爲只有這那個時候 GUI 纔得到完整的定義。這意味着只有在後期纔可能發現潛在的錯誤。
  4. 一般的開發人員可能沒有自動的 GUI 測試工具。因此,當一個開發人員對代碼進行更改 時,沒有一種簡單的方法來重新測試受到影響的子系統。這實際上不利於進行良好的測試。如果開 發人員具有自動的代碼級單元測試工具,開發人員就能夠很容易地運行這些工具以確保所做的更改 不會破壞已經存在的功能。
  5. 如果添加了自動構建功能,則在自動構建過程中添加一個自動的單元測試工具是非常容易 的事情。當完成這些設置以後,整個系統就可以有規律地進行重建,並且迴歸測試幾乎不需要人的 參與。

另外,我們必須強調,使用 EJB 和 Web 服務進行分佈式的、基於組件的開發使得測試單個組 件變得非常必要。如果沒有“GUI”需要測試,您就必須進行低級(lower-level)測試 。最好以這種方式開始測試,省得當您將分佈式的組件或 Web 服務作爲您的應用程序的一部分時 ,您不得不花費心思重新進行測試。

總之,通過使用自動的單元測試,能夠很快地發現系統的缺陷,並且也易於發現這些缺陷,使 得測試工作變得更加系統化,因此整體的質量也得以提高。





回頁首


4. 按照規範來進行開發,而不是按照應用服務器來進 行開發。

要將規範熟記於心, 如果要背離規範,需經過慎密的考慮後纔可以這樣做。這是因爲當您背離規則的時候,您所做的事 情往往並不是您應該做的事情。


當您要背離 Java EE 允許您做的事情的時候,這很容易讓使您遭受不幸。我們發現有一 些開發人員鑽研一些 Java EE 允許之外的東西,他們認爲這樣做可以“稍微”改善 Java EE 的性能,而他們最終只會發現這樣做會引起嚴重的性能問題,或者在以後的移植(從一個 廠商到另一個廠商,或者是更常見的從一個版本到另一個版本)中會出現問題。實際上,這種移植 問題是如此嚴重,以致 [Beaton] 將此原則稱爲移植工作的基本最佳實踐。

現在有好幾個地方如果不直接使用 Java EE 提供的方法肯定會產生問題。一個常見的例子就是 開發人員通過使用 JAAS 模塊來替代 Java EE 安全性,而不是使用內置的遵循規範的應用服務器 機制來進行驗證和授權。要注意不要脫離 Java EE 規範提供的驗證機制。如果脫離了此規範,這 將是系統存在安全漏洞以及廠商兼容性問題的主要原因。類似地,要使用 Servlet 和 EJB 規範提 供的授權機制,並且如果您要偏離這些規範的話,要確保使用規範定義的 API(例如 getCallerPrincipal())作爲實現的基礎。通過這種方式,您將能夠利用廠商提供的強安全性基礎 設施,其中,業務要求需要支持複雜的授權規則。(有關授權的更詳細內容,請參見 [Ilechko]。)

其他常見的問題包括使用不遵循 Java EE 規範的持久性機制(這使得事務管理變得困難)、在 Java EE程序中使用不適當的 J2SE 方法(例如線程或 singleton),以及使用您自己的方法解決 程序到程序(program-to-program)的通信,而不是使用 Java EE 內在支持的機制(例如 JCA、 JMS 或 Web 服務)。當您將一個遵循 Java EE 的服務器移植到其他的服務器上,或者移植到相同 服務器的新版本上,上述的設計選擇將會造成無數的問題。使用 Java EE 之外的元素,通常會導 致一些細微的可移植性問題。唯一要背離規範的情況是,當一個問題在規範的範圍內無法解決的時 候。例如,安排執行定時的業務邏輯在 EJB2.1 出現之前是一個問題。在類似這樣的情況下,我們 建議當有廠商提供的解決方案時就使用廠商提供的解決方案(例如 WebSphere Application Server Enterprise 中的 Scheduler 工具),而在沒有廠商提供的解決方案時就使用第三方提供 的工具。當然,現在的 EJB 規範提供了基於時間的函數,所以我們鼓勵使用這些標準接口。如果 使用廠商提供的解決方案,應用程序的維護以及將其移植到新的規範版本將是廠商的問題,而不是 您的問題。

最後,要注意不要太早地採用新技術。太過於熱衷採用還沒有集成到 Java EE 規範的其他部分 或者還沒有集成到廠商的產品中的技術常會帶來災難性的後果。支持是關鍵的——如果 您的廠商不直接支持某種特定的技術,那麼您在採用此技術時就應該非常謹慎。有些人(尤其是開 發人員)過分關注於簡化開發過程,忽略了依賴大量本組織之外開發的代碼的長期後果,而供應商 並不支持這些代碼。我們發現,許多項目團隊沉迷於新技術(例如最新的開放源代碼框架),並很 快地依賴於它,卻沒有考慮它對業務帶來的實際代價。坦白地說,對於使用您的供應商所提供的產 品之外的任何技術的決策,都應該由企業組織結構中的各個部門、業務團隊和法律團隊(或您的環 境中的等同機構)仔細地進行評審,這與正常的產品購買決策完全相同。畢竟,我們中的大多數人 是在解決業務問題,而不是推進技術的發展。





回頁首


5. 從一開始就計劃使用 Java EE 安全性。

啓用 WebSphere 安全 性。這使您的 EJB 和 URL 至少可以讓所有授權用戶訪問。不要問爲什麼——照着做就 是了。


在與我們合作的客戶中,一開始就打算啓用 WebSphere Java EE 安全性的顧客是非常少 的,這一點一直讓我們感到吃驚。據我們估計大約只有 50% 的顧客一開始就打算使用此特性。例 如,我們曾與一些大型的金融機構(銀行、代理等等)合作過,他們也沒有打算啓用安全性。幸運 的是,這種問題在部署之前的檢查時就得以解決。

不使用 Java EE 安全性是件危險的事情。假設您的應用程序需要安全性(幾乎所有的應用程序 都需要),敢打賭您的開發人員能夠構建出自己的安全性基礎設施,其比您從 Java EE 廠商那裏 買來的更好。這可不是個好的賭博遊戲。爲分佈式的應用程序提供安全性是異常困難的。例如,您 需要使用網絡安全加密令牌控制對 EJB 的訪問。以我們的經驗看來,大多數自己構建的安全性基 礎設施是不安全的,並且有重大的缺陷,這使產品系統極其脆弱。(有關更詳細的信息,請參考 [Barcia] 的第 18 章。)

一些不使用 Java EE 安全性的理由包括:擔心性能的下降,相信其他的安全性(例如 IBM Tivoli® Access Manager 和 Netegrity SiteMinder)可以取代 Java EE 安全性,或者是不 知道 WebSphere Application Server 安全特性及功能。不要陷入這些陷阱之中。尤其是,儘管像 Tivoli Access Manager 這樣的產品能夠提供優秀的安全特性,但是僅僅其自身不可能保護整個 Java EE 應用程序。這些產品必須與 Java EE 應用服務器聯合起來纔可能全面地保護您的系統。

其他一種常見的不使用 Java EE 安全性的原因是,基於角色的模型沒有提供足夠的粒度訪問控 制以滿足複雜的業務規則。儘管事實是這樣的,但這也不應該成爲不使用 Java EE 安全性的理由 。相反地,應該將 Java EE 驗證及 Java EE 角色與特定的擴展規則結合起來。如果複雜的業務規 則需要做出安全性決策,那就編寫相應的代碼,其安全性決策要基於可以直接使用的以及可靠的 Java EE 驗證信息(用戶 ID 和角色)。(有關授權的更詳細的信息,請參見 [Ilechko]。)





回頁首


6. 創建您所知道的。

反覆的開發工作將使 您能夠逐漸地掌握所有的 Java EE 模塊。要從創建小而簡單的模塊開始而不是從一開始就馬上涉 及到所有的模塊。


我們必須承認 Java EE 是龐大的體系。如果一個開發團隊只是開始使用 Java EE,這將 很難一下子就能掌握它。在 Java EE 中有太多的概念和 API 需要掌握。在這種情況下,成功掌握 Java EE 的關鍵是從簡單的步驟開始做起。

這種方法可以通過在您的應用程序中創建小而簡單的模塊來得到最好的實現。如果一個開發團 隊通過創建一個簡單的域模型以及後端的持久性機制(也許使用的是 JDBC),並且對其進行了完 整的測試,這會增強他們的自信心,於是他們會使用該域模型去掌握使用 Servlet 和 JSP 的前端 開發。如果一個開發團隊發現有必要使用 EJB,他們也會類似地開始在容器管理的持久性 EJB 組 件之上使用簡單的會話 Facade,或者使用基於 JDBC 的數據訪問對象(JDBC-based Data Access Objects,DAO),而不是跳過這些去使用更加複雜的構造(例如消息驅動的 Bean 和 JMS)。

這種方法並不是什麼新方法,但是很少有開發團隊以這種方式來培養他們的技能。相反地,多 數開發團隊由於嘗試馬上就構建所有的模塊,同時涉及 MVC 中的視圖層、模型層和控制器層,這 樣做的結果是他們往往會陷入進度的壓力之中。他們應該考慮一些敏捷(Agile)開發方法,例如 極限編程(XP),這種開發方法採用一種增量學習及開發方法。在 XP 中有一種稱爲 ModelFirst [Wiki] 的過程,這個過程涉及到首先構建域模型作爲一種機制來組 織和實現用戶場景。基本說來,您要構建域模型作爲您要實現的用戶場景的首要部分,然後在域模 型之上構建一個用戶界面(UI)作爲用戶場景實現的結果。這種方法非常適合讓一個開發團隊一次 只學到一種技術,而不是讓他們同時面對很多種情況(或者讓他們讀很多書),這會令他們崩潰的 。

還有,對每個應用程序層重複的開發可能會包含一些適當的模式及最佳實踐。如果您從應用程 序的底層開始應用一些模式(如數據訪問對象和會話 Facade),您就不應該在您的JSP和其他視圖 對象中使用域邏輯。

最後,當您開發一些簡單的模塊時,在開始的初期就可以對您的應用程序進行性能測試。如果 直到應用程序開發的後期才進行性能測試的話,這往往會出現災難性的後果,正如 [Joines] 所述。





回頁首


7. 當使用 EJB 組件時,始終使用會話 Facade。

在體系結構合適的情 況下,使用本地 EJB。


當使用 EJB 組件時,使用會話 Facade 是一個確認無疑的最佳實踐。實際上,這個通用 的實踐被廣泛地應用到任何分佈式技術中,包括 CORBA、EJB 和 DCOM。從根本上講,您的應用程 序的分佈“交叉區域”越是底層化,對小塊的數據由於多次重複的網絡中繼造成的時間 消耗就越少。要達到這個目的的方法是,創建大粒度的 Facades 對象,這個對象包含邏輯子系統 ,因而可以通過一個方法調用就可以完成一些有用的業務功能。這種方法不但能夠降低網絡開銷, 而且在 EJB 內部通過爲整個業務功能創建一個事務環境也可以大大地減少對數據庫的訪問次數。 [Alur] 對這種模式進行了規範的表示,[Fowler](並且包括除 EJB 之外的情況)和 [Marinescu] 也對其進行了描述(請參見參考資料)。細心的讀者會發現,這實際上正是面向服務的體系結 構 (SOA) 中的核心原則之一。

EJB 本地接口(從 EJB 2.0 規範開始使用)爲共存的 EJB 提供了性能優化方法。本地接口必 須被您的應用程序顯式地進行訪問,這需要代碼的改變和防止以後配置 EJB 時需要應用程序的改 變。如果您確定 EJB 調用始終是本地的,那麼可以充分利用本地 EJB 的優化。然而,會話 Facade 本身的實現(典型例子如無狀態會話 Bean)應該設計爲遠程接口。通過這種方式,其他的 客戶端可以遠程地使用 EJB 本身,而不會破壞現有的業務邏輯。因爲 EJB 可以同時具有本地和遠 程接口,所以這是完全可以實現的。

爲了性能的優化,可以將一個本地接口添加到會話 Facade。這樣做利用了這樣一個事實,在大 多數情況下(至少在 Web 應用程序中),您的 EJB 客戶端和 EJB 會共同存在於同一個 Java 虛 擬機(JVM)中。另外一種情況是,如果會話 Facade 在本地被調用,可以使用 Java EE 應用服務 器配置優化(configuration optimizations),例如 WebSphere 中的“No Local Copies”。然而,您必須注意到這些可供選擇的方案會將交互方法從按值傳遞(pass-by- value)改變爲按引用傳遞(pass-by-reference)。這可能會在您的代碼中產生很微妙的錯誤。最 好使用本地 EJB,因爲對於每個 Bean 而言,其行爲是可以控制的,而不會影響到整個應用服務器 。

如果在您的會話 Facade 中使用遠程接口(而不是本地接口),您也可以將同樣的會話 Facade 在 Java EE 1.4 中以兼容的方式作爲 Web 服務來配置。(這是因爲 JSR 109,Java EE 1.4 中的 Web 服務部署部分,要求使用無狀態會話 Bean 的遠程接口作爲 EJB Web 服務和 EJB 實現的接口 。)這樣做是值得的,因爲這樣做可以爲您的業務邏輯增加客戶端類型的數量。





回頁首


8. 使用無狀態會話 Bean,而不是有狀態會話 Bean。

這樣做可以使您的系 統更經得起故障轉移。使用 HttpSession 存儲和用戶相關的狀態。


以我們的觀點來看,有狀態會話 Bean 的概念已經過時了。如果您仔細對其考慮一下, 一個有狀態會話 Bean 實際上與一個 CORBA 對象在體系結構上是完全相同的,無非就是一個對象 實例綁定到一個服務器,並且依賴於服務器來管理其生命週期。如果服務器關閉了,這種對象也就 不存在,那麼這個 Bean 的客戶端的信息也就不存在。

Java EE 應用服務器爲有狀態會話 Bean 提供的故障轉移能夠解決一些問題,但是有狀態的解 決方案沒有無狀態的解決方案易於擴展。例如,在 WebSphere Application Server 中,對無狀態 會話 Bean 的請求,是通過對部署無狀態會話的成員集羣進行平衡加載來實現。相反地,Java EE 應用服務器不能對有狀態 Bean 的請求進行平衡加載。這意味着您的集羣中的服務器的加載過程會 是不均衡的。此外,使用有狀態會話 Bean 將會再添加一些狀態到您的應用服務器上,這也是不好 的做法。有狀態會話 Bean 增加了系統的複雜性,並且在出現故障的情況下使問題變得複雜化。創 建健壯的分佈式系統的一個關鍵原則是儘量使用無狀態行爲。

因此,我們建議對大多數應用程序使用無狀態會話 Bean 方法。任何在處理時需要使用的與用 戶相關的狀態應該以參數的形式傳送到 EJB 的方法中(並且通過使用一種機制如 HttpSession 來 存儲它)或者從持久性的後端存儲(例如通過使用實體 Bean)作爲 EJB 事務的一部分來進行檢索 。在合適的情況下,這個信息可以緩存到內存中,但是要注意在分佈式的環境中保存這種緩存所潛 在的挑戰性。緩存非常適合於只讀數據。

總之,您要確保從一開始就要考慮到可擴展性。檢查設計中的所有設想,並且考慮到當您的應 用程序要在多個服務器上運行時,是否也可以正常運行。檢查設計中所有的假設,判斷如果您的應 用程序運行於多個服務器之上,它們是否依然成立。這個規則不但適合上述情況的應用程序代碼, 也適用於如 MBean 和其他管理接口的情況。

避免使用有狀態性不只是對 IBM/WebSphere 的建議,這是一個基本的 Java EE 設計原則。請 參見 [Jewell] 的 Tyler Jewell 對有狀態 Bean 的批評,其觀點和上述 的觀點是相同的。





回頁首


9. 使用容器管理的事務。

學習一下 Java EE 中 的兩階段提交事務,並且使用這種方式,而不是開發您自己的事務管理。容器在事務優化方面幾乎 總是比較好的。


使用容器管理的事務(CMT)提供了兩個關鍵的優勢(如果沒有容器支持這幾乎是不可能 的):可組合的工作單元和健壯的事務行爲。

如果您的應用程序代碼顯式地使用了開始和結束事務(也許使用 javax.jts.UserTransaction 或者甚至是本地資源事務),而將來的要求需要組合模塊(也許會是代碼重構的一部分),這種情 況下往往需要改變事務代碼。例如,如果模塊 A 開始了一個數據庫事務,更新數據庫,隨後提交 事務,並且有模塊 B 做出同樣的處理,請考慮一下當您在模塊 C 中嘗試使用上述兩個模塊,會出 現什麼情況呢?現在,模塊 C 正在執行一個邏輯動作,而這個動作實際上將調用兩個獨立的事務 。如果模塊 B 在執行中失敗了,而模塊 A 的事務仍然能被提交。這是我們所不希望出現的行爲。 如果,相反地,模塊 A 和模塊 B 都使用 CMT 的話,模塊 C 也可以開始一個 CMT(通常通過配置 描述符),並且在模塊 A 和模塊 B 中的事務將是同一個事務的隱含部分,這樣就不再需要重寫復 雜的代碼了。

如果您的應用程序在同一個操作中需要訪問多種資源,您就要使用兩階段提交事務。例如,如 果從 JMS 隊列中刪除一個消息,並且隨後更新基於這條消息的紀錄,這時,要保證這兩個操作都 會執行或都不會執行就變得尤爲重要。如果一條消息已經從隊列中被刪除,而系統沒有更新與此消 息相關的數據庫中的記錄,那麼這種系統是不一致的。一些嚴重的客戶及商業糾紛源自不一致的狀 態。

我們時常看到一些客戶應用程序試圖實現他們自己的解決方案。也許會通過應用程序的代碼在 數據庫更新失敗的時候“撤銷”對隊列的操作。我們不提倡這樣做。這種實現要比您最 初的想象複雜得多,並且還有許多其他的情況(想象一下如果應用程序在執行此操作的過程中突然 崩潰的情況)。作爲替代的方式,應該使用兩階段提交事務。如果您使用 CMT,並且在單一的 CMT 中訪問兩階段提交的資源(例如 JMS 和大多數數據庫),WebSphere 將會處理所有的複雜工作。 它將確保整個事務被執行或者都不被執行,包括系統崩潰、數據庫崩潰或其他的情況。其實現在事 務日誌中保存着事務狀態。當應用程序訪問多種資源的時候,我們怎麼強調使用 CMT 事務的必要 性都不爲過。如果您所訪問的資源不支持兩階段提交,那麼您當然就沒有別的選擇了,只能使用一 種比較複雜的方法,但是您應該儘量避免這種情況。





回頁首


10. 將 JSP 作爲表示層技術的首選。

只有在需要多種表示 輸出類型,並且輸出類型被單一的控制器及後端支持時才使用 XML/XSLT。


我們常聽到一些爭論說,爲什麼您選擇 XML/XSLT 而不是 JSP 作爲表示層技術,因爲 JSP“允許您將模型和視圖混合在一起”,而 XML/XSLT 不會有這種問題。遺憾的是, 這種觀點並不完全正確,或者至少不像白與黑那樣分的清楚。實際上,XSL 和 XPath 是編程語言 。事實上,XSL 是圖靈完備的(Turing-complete),儘管它不符合大多數人定義的編程語言,因 爲它是基於規則的,並且不具備程序員習慣的控制工具。

問題是既然給予了這種靈活性,開發人員就會利用這種靈活性。儘管每個人都認同 JSP 使開發 人員容易在視圖中加入“類似模型”的行爲,而實際上,在 XSL 中也有可能做出一些 同樣的事情。儘管從 XSL 中進行訪問數據庫這樣的事情會非常困難,但是我們曾經見到過一些異 常複雜的 XSLT 樣式表執行復雜的轉換,這實際上是模型代碼。

然而,應該選擇 JSP 作爲首選的表示技術的最基本的原因是,JSP 是現在支持最廣泛的、也是 最被廣泛理解的 Java EE 視圖技術。而隨着自定義標記庫、JSTL 和 JSP2.0 的新特性的引入,創 建 JSP 變得更加容易,並且不需要任何 Java 代碼,以及可以將模型和視圖清晰地分離開。在一 些開發環境中(如 IBM Rational Application Developer)加入了對 JSP(包括對調試的支持) 的強大支持,並且許多開發人員發現使用 JSP 進行開發要比使用 XSL 更加簡單,主要是因爲 JSP 是基於例程的,而不是基於規則的。儘管 Rational Application Developer 支持 XSL 的開發, 但一些支持 JSP 的圖形設計工具及其他特徵(尤其在 JSF 這樣的框架下)使得開發人員可以以所 見即所得的方式進行 JSP 的開發,而使用 XSL 有時不容易做到。

然而,這並不表示您絕不 應該使用 XSL。在一些情況下,XSL 能夠表示一組固定的 數據,並且可以基於不同的樣式表(請參見 [Fowler])來以不同的方式顯示這些數據的能力是顯示視圖的最佳 解決方案。然而,這只是一種特殊的情況,而不是通用的規則。如果您只是生成 HTML 來表達每一 個頁面,那麼在大多數情況下,XSL 是一種不必要的技術,並且,它給您的開發人員所帶來的問題 遠比它所能解決的問題多。





回頁首


11. 當使用 HttpSession 時,儘量只將當前事務所 需要的狀態保存在其中,其他內容不要保存在 HttpSession 中。

啓用會話持久性。


HttpSessions 對於存儲應用程序狀態信息是非常有用的。其 API 易於使用和理解。遺 憾的是,開發人員常常遺忘了 HttpSession 的目的——用來保持臨時的用戶狀態。它 不是任意的數據緩存。我們已經見到過太多的系統爲每個用戶的會話放入了大量的數據(達到兆字 節)。如果同時有 1000 個登錄系統的用戶,每個用戶擁有 1MB 的會話數據,那麼就需要 1G 或 者更多的內存用於這些會話。保持這些 HTTP 會話數據較小。不然的話,您的應用程序的性能將會 下降。一個大約比較合適的數據量應該是每個用戶的會話數據在 2K-4K 之間。這不是一個硬性的 規則。8K 仍然沒有問題,但是顯然會比 2K 時的速度要慢。一定要注意,不要使 HttpSession 變 成數據堆積的場所。

一個常見的問題是使用 HttpSession 緩存一些很容易再創建的信息,如果有必要的話。由於會 話是持久性的,進行不必要的序列化以及寫入數據是一種很奢侈的決定。相反地,應該使用內存中 的哈希表來緩存數據,並且在會話中保存一個對此數據進行引用的鍵。這樣,如果不能成功登錄到 另外的應用服務器的話,就可以重新創建數據。(有關更詳細的信息,請參見 [Brown2]。)

當談及會話持久性時,不要忘記要啓用這項功能。如果您沒有啓用會話持久性,或者服務器因 爲某種原因停止了(服務器故障或正常的維護),則所有此應用服務器的當前用戶的會話將會丟失 。這是件令人非常掃興的事情。用戶不得不重新登錄,並且重新做一些他們曾經已經做過的事情。 相反地,如果啓用了會話持久性,WebSphere 會自動將用戶(以及他們的會話)移到另外一個應用 服務器上去。用戶甚至不知道發生了這樣的事情。我們曾經見到過一些產品系統,因爲本地代碼中 存在令人難以忍受的錯誤(不是 IBM 的代碼!)而經常崩潰,在這種情況下,上述功能仍然可以 運行良好。





回頁首


12. 充分利用應用服務器中不需要修改代碼的特性。

使用某些特性(如 WebSphere Application Server 緩存和 Prepared Statement 緩存)可以極大地提高性能,並且 使得開銷最小。


前面的最佳實踐 4 清楚地描述了這樣一種案例,即關於爲什麼應該謹慎的使用可能修改 代碼的應用服務器特定的特性。它使得難以實現可移植性,並且可能給版本的遷移帶來困難。然而 ,特別是在 WebSphere Application Server 中,有一套應用服務器特定的特性,您可以並且應該 充分地利用它們,因爲它們不會修改您的代碼。您應該按照規範來編寫代碼,但如果您瞭解這些特 性以及如何正確地使用它們,那麼您就可以利用它們顯著地改善性能。

作爲這個最佳實踐的一個示例,在 WebSphere Application Server 中,您應該開啓動態緩存 ,並且使用 Servlet 緩存。系統性能可以得到很大的提高,而開銷是最小的,並且不影響編程模 型。通過緩存來提高性能的好處是衆所周知的事情。遺憾的是,當前的 Java EE 規範沒有包括一 種用於 Servlet/JSP 緩存的機制。然而,WebSphere 提供了對頁面以及片斷緩存的支持,這種支 持是通過其動態緩存功能來實現的,並且不需要對應用程序作出任何改變。其緩存的策略是聲明性 的,而且其配置是通過 XML 配置描述符來實現的。因此,您的應用程序不會受到影響,並保持與 Java EE 規範的兼容性和移植性,同時還從 WebSphere 的 Servlet 及 JSP 的緩存機制中得到性 能的優化。

從 Servet 及 JSP 的動態緩存機制得到的性能的提高是顯而易見的,這取決於應用程序的特性 。Cox 和 Martin [Cox] 指出,對一個現有的 RDF(資源描述格式)站點摘要 (RSS) Servlet 使用動態緩存時,其性能可以提高 10%。請注意這個實驗只涉及到一個簡單的 Servlet, 這個性能的增長量可能並不能反映一個複雜的應用程序。

爲了更多地提高性能,將 WebSphere Servlet/JSP 結果緩存與 WebSphere 插件 ESI Fragment 處理器、IBM HTTP Server Fast Response Cache Accelerator (FRCA) 和 Edge Server 緩存功能 集成在一起。對於繁重的基於讀取的工作負荷,通過使用這些功能可以得到許多額外的好處。(請 參見參考資料的 [Willenborg] 和 [Bakalova] 中描述的性能的提 高。)

作爲該原則的另一個示例(我們常常發現客戶不使用它,僅僅是因爲他們根本不知道它的存在 ),在編寫 JDBC 代碼時可以利用 WebSphere Prepared Statement Cache。在缺省情況下,當您 在 WebSphere Application Server 中使用 JDBC PreparedStatement 時,它將對該語句進行一次 編譯,然後將其放到緩存中以便再次使用,不僅可以在創建 PreparedStatement 的同一方法中重 用,還可以跨程序重用,只要其中使用了相同的 SQL 代碼或者另一個 PreparedStatement。省去 重新編譯的步驟可以極大降低調用 JDBC 驅動程序的次數,並且提高應用程序的性能。要利用這個 特性,您只需編寫相應的 JDBC 代碼以使用 PreparedStatements,而不需要進行任何其他工作。 在編寫代碼時,使用 PreparedStatement 代替常規的 JDBC Statement 類(它使用了純的動態 SQL),您就可以實現性能的增強,而不會損失任何可移植性。





回頁首


13. 充分利用現有的環境。

提供一個 Java EE EAR 和可配置的安裝腳本,而不是黑盒二進制安裝程序。


在大多數的實際場景中,大量的 WebSphere Application Server 用戶在相同的共享單 元中運行多個應用程序。這意味着,如果您提供一個需要安裝的應用程序,那麼它必須能夠合理地 安裝到現有的基礎設施中。這意味兩個方面:首先,您必須限制關於環境的假設的數目,並且因爲 您無法預料到所有的情況,所以您的安裝過程必須是可見的。這裏所說的可見是指,不應該提供二 進制可執行文件形式的安裝程序。執行安裝任務的管理員需要清楚安裝過程對他們的單元所進行的 操作。爲了實現這種方式,您應該提供一個 EAR 文件(或者一組 EAR 文件)以及相關的文檔和安 裝腳本。這些腳本應該具有可讀性,以便安裝程序能夠知道它們需要執行的操作,並對腳本的內容 進行驗證以確保不會執行任何危險的操作。在有些情況下,腳本並不合適,用戶可能需要使用一些 曾用過的其他方法來安裝 EAR,這表示您必須記錄安裝程序所完成的工作!





回頁首


14. 充分利用應用服務器環境所提供的服務質量。

設計可使用 WebSphere Application Server Network Deployment 集羣的應用程序。


我們已經介紹了利用 WebSphere Application Server 安全和事務支持的重要性。還有 一個更重要的、常常被我們忽視的問題,即集羣。需要將應用程序設計爲能夠運行於集羣的環境。 大多數實際的環境需要通過集羣來實現可擴展性和可靠性。無法進行集羣的應用程序很快會導致災 難的出現。

與集羣緊密相關的是支持 WebSphere Application Server Network Deployment。如果您正在 構建一個應用程序並打算將它賣給其他人,請確保您的應用程序可以運行於 WebSphere Application Server Network Deployment,而不僅僅是單個服務器版本。





回頁首


15. 利用 Java EE,不要欺騙。

致力於構建真正利用 Java EE 功能的 Java EE 應用程序。


有件非常煩人的事情我們曾多次遇到過,某個應用程序聲稱可以運行於 WebSphere 中, 但它並不是一個真正的 WebSphere 應用程序。我們曾見過幾個這樣的示例,其中有一小段代碼( 可能是一個 Servlet)位於 WebSphere Application Server 中,而其餘所有的應用程序邏輯實際 上位於單獨的進程中,例如一個以 Java、C、C++ 或其他語言(沒有使用 Java EE)編寫的守護進 程負責完成實際的工作。這並不是一個真正的 WebSphere Application Server 應用程序。對於這 樣的應用程序,WebSphere Application Server 所提供的幾乎所有的服務質量都不可用。對於那 些認爲這是 WebSphere Application Server 應用程序的人來說,他們會突然的醒悟過來,原來並 非如此。





回頁首


16. 安排進行版本更新。

更改是在所難免的。 安排新的發行版和修復程序更新,以便您的客戶能夠獲得最新的版本。


WebSphere Application Server 在不斷地發展,所以 IBM 定期地給出 WebSphere Application Server 的修復程序,這是很正常的,並且 IBM 還定期地發佈新的主要版本。您需要 爲此做好安排。這會影響到兩類開發組織:內部開發人員和第三方應用程序供應商。基本的問題是 相同的,但對兩者的影響則有所不同。

首先考慮修復程序。IBM 定期發佈建議更新,以修復產品中已 發現的錯誤。儘管不太可能始終運行於最新的級別,但請注意,不要隔得太久。那麼究竟“ 隔多久”是可以接受的呢?對於這個問題沒有什麼正確的答案,但是您應該安排好對幾個月 內的發行版進行修復級別更新。是的,這表示一年要更新好幾次。內部開發人員可以忽略某些修復 級別,一次僅支持一個修復級別,以降低測試成本。應用程序供應商則沒有這麼幸運。如果您是應 用程序供應商,那麼您同時需要支持多種修復級別,以便您的客戶能夠將您的軟件與其他軟件一同 運行。如果您僅支持一種修復級別,那麼很可能無法找到同時兼容於多種產品的修復級別。實際上 對於供應商而言,最好的方法是使用支持“向上兼容修復程序”的模型。IBM 使用了這 種方法來支持所集成的來自其他供應商的產品(如 Oracle®、Solaris™ 等等)。有關 更詳細的信息,請參考我們的支持策略

下面再考慮一下主要版本更新。IBM 定期地發佈新的主要發行版,其中對我們的產品進行了主 要的功能更新。我們暫時繼續支持舊的主要發行版,但不會太久。這意味着您必須安排從一個主要 發行版轉到另一個主要發行版。這是不可避免的,並且應該在您的成本模型中加以考慮。如果您是 供應商,這意味着您必須經常地對您的產品進行更新,以支持新的 WebSphere Application Server 版本,否則您的客戶將停滯於不受支持的 IBM 產品,我們曾多次碰到過這種情況!如果您 正從供應商處購買產品,我們鼓勵您要留心您的供應商,以確保他們承諾支持 IBM 產品新的版本 。停滯於不受支持的軟件是一種非常危險的情況。





回頁首


17. 在代碼中所有關鍵的地方,使用標準的日誌框架 記錄程序的狀態。

這包括異常處理程序 。使用像 JDK 1.4 Logging 或 Log4J 這樣的日誌框架。


有些時候,日誌記錄是最乏味的工作,降低了編程的價值,但是這樣做可以減少調試的 時間,並儘快地完成相應的任務。根據一般的經驗,在每個過渡的地方,需要進行日誌記錄。當您 將參數從一個方法傳遞到另一個方法,或從一個類傳遞到另一個類,需要進行日誌記錄。在對一個 對象進行某種轉換時,需要進行日誌記錄。在碰到不解之處時,需要進行日誌記錄。

在決定了進行日誌記錄之後,需要選擇一種合適的框架。實際上有許多選擇,但是我們偏愛 JDK 1.4 Trace API,因爲它們已全面地集成到了 WebSphere Application Server 跟蹤子系統中 ,並且是基於標準的。





回頁首


18. 在完成相應的任務後,請始終進行清理。

如果您從池中獲取了 一個對象,請始終確保將其返回到池中。


無論運行於開發、測試或生產環境中,我們發現 Java EE 應用程序最常見的錯誤之一是 內存泄漏。絕大部分情況是因爲開發人員忘了關閉連接(大多數情況下是 JDBC 連接)或將對象返 回到池中。對於任何需要顯式關閉的或需要返回到池中的對象,請確保進行了這樣的操作。不要編 寫出這樣糟糕的代碼。





回頁首


19. 在開發和測試過程中遵循嚴格的程序。

這包括採用和遵循軟 件開發方法學。


大型系統的開發是非常困難的,所以應該十分謹慎。但是,我們常常發現一些團隊疏於 管理、或者不能全心全意地遵循相關的開發方法(這些方法可能不適用於他們正在進行的開發類型 )、或者他們並沒有很好地理解這一點。最爲糟糕的可能是嘗試每個月更換不同的開發方法,在單 個項目的生命週期中,一個團隊從 RUP 改變爲 XP,以及一些其他敏捷方法。

總之,對於大多數團隊而言,只要團隊成員能夠很好地理解、嚴格地執行、並根據特定的技術 本質和使用該方法的團隊進行適當的調整,那麼幾乎任何一種方法都是有效的。對於那些尚未採用 任何方法、或者那些不能夠完全地利用所選方法的團隊,我們建議他們參考一些優秀的著作,如 [Jacobson]、[Beck1] 或 [Cockburn]。另一個有價值的信息來源是最近公佈的用於 Eclipse Process Framework [Eclipse] 的 OpenUP 插件。對於這個已經介紹過的主題,我們不想做過多的 重複,建議讀者參考 [Hambrick] 和 [Beaton2](請參見參考資料)。





回頁首


結束語

在這個簡短的摘要中,我們已經向您介紹了 Java EE 中的核心模式和最佳實踐,它們使得 Java EE 開發成爲一種可管理的過程。儘管我們並沒有給出所有在實踐中使用這些模式的必要細節 ,但是我們希望能夠給您足夠的指點和指導,以幫助您決定下一步要做什麼。





回頁首


致謝

感謝所有最初提出這些模式和最佳實踐的人(以及下面提到的人),此外還要感謝 John Martinek、Paul Ilechko、Bill Hines、Dave Artus 和 Roland Barcia 對本文的審閱。

原文作者如下:

 Keys Botzum, 高級技術人員 , IBM
Kyle Brown, 傑出工程師, IBM
Ruth Willenborg ([email protected]), 高級技術人員, IBM
Albert Wong, I/T 架構師, IBM
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章