posts - 189,comments - 115,trackbacks - 0
          一篇關于JSF的入門文章
          轉貼?

          摘要:JavaServer?Faces作為一種新興的Web表現層框架,正在受到越來越多的關注。本文描述了JSF的幾大優勢,以及這些優勢所帶來的Web開發的重大變革,從而試圖說明JSF將會在眾多競爭者中脫穎而出,成為Web表現層框架的主流。

            1.?引子:我與JSF的第一次親密接觸

            2004年3月,當我著手開發我的第一個Web程序時,我選擇了JSP。作為一個傳統的桌面程序員,而且是老程序員,向Web程序員的轉變是異常痛苦的。3個月的時間,程序總算完成了,但從此對JSP恨之入骨。凌亂的書寫格式,數據和界面的混雜,尤其是嵌入到頁面里的Scriptlet,讓我搞不清自己是編程序的還是寫網頁的。?

            因此,當接到第二版的開發任務時,我毫不猶豫地放棄了JSP,尋找一種替代技術。上網一搜,卻發現框架多如牛毛,評論文章各執一詞,莫衷一是,讓我徹底迷失了。猶豫搖擺不定之際,sun的J2EE?Tutorial文檔中關于JavaServer?Faces技術的介紹吸引了我:UI、component、event、listener這些在桌面程序中熟悉的字眼,讓我在Web開發中找到了桌面程序員的一些感覺。

            象開發桌面程序那樣開發web程序,這是我選擇JSF的初衷。基于這樣膚淺的認識,跌跌撞撞上路了,在工期和新技術的雙重壓力之下,超負荷的工作令人透不過氣來,但每每從JSF中發掘出令人驚喜的新特性,又給我帶來極大的滿足感。第二版終于完成時,日歷恰好翻過一個整月。JSF帶來的效率提升是顯著的。

            事實上,到現在為止,我對于JSF還只能說是初步了解,遠未達到掌握,更談不上精通,但這并不妨礙我視JSF為Web開發的首選框架。尤其是對于新手,如果還沒有在Struts、Tiles、Spring、Tapestry等框架中走得太遠,那么,集中你有限的精力踏上JSF之路吧。?

            2.?JSF優勢之一:UI組件(UI-component)

            UI組件(UI-component)一直是桌面程序的專利,web程序中,雖然HTML定義了基本的UI標簽,但要使這些UI標簽像UI組件那樣工作,還需要很多代碼片斷來處理數據及其表現形式,而且有效地組織這些代碼片斷使其協調一致也是一件繁瑣的工作。JSF的UI組件是真正意義上的UI組件,能極大地簡化程序員的工作,例如,在頁面上放置一個文本輸入框,這個輸入框立即具備了數據填充、界面更新、事件偵聽、動作觸發、有效性檢查和類型轉換的功能。更為重要的是,程序員只需根據業務邏輯編寫核心業務代碼,JSF會保證代碼在合適的時候被執行,完全不用考慮代碼與代碼之間該如何來配合。?

            3.?JSF優勢之二:事件驅動模式

            事件是面向對象方法的重要組成部分,對象之間通過事件進行溝通和交流,使得一個或多個對象能夠對另一個對象的行為作出響應,共同合作去完成一項業務邏輯。通常,編寫Web程序時,程序員要為對象之間的溝通設計機制,編寫代碼。雖然溝通的內容屬于業務邏輯,但溝通的機制顯然與業務沒有太大關系,程序員因此為業務邏輯之外的功能浪費了時間。JSF改變了這種狀況。JSF的事件和偵聽模式與大家熟悉的Javabean的事件模式類似,有Java基礎的程序員并不需要學習任何新的東西。JSF的UI組件可以產生事件,例如,當頁面上一個文本輸入框的內容被修改時,會發出一個“值改變事件”。另一個對象如果對“值改變事件”感興趣,只需注冊為該對象的偵聽者,并編寫處理例程,即可命令JSF在事件發生時自動調用處理例程。JSF做了所有該做的事,留給程序員的只有業務邏輯代碼的編寫。?

            4.?JSF優勢之三:用戶界面到業務邏輯的直接映射

            舉個例子,表單提交是Web編程最常見的任務,也是最復雜的任務之一。當用戶在網頁上點擊“確定”按鈕時,瀏覽器將生成一個HTTP請求,發往服務器端的某個Servlet,執行該Servlet的service方法。在service方法中,HTTP請求需要經歷解碼、類型轉換、有效性驗證、狀態保存、數據更新等環節,處理這些環節的所有細節,對程序員來說是沉重的負擔。在JSF下,這些工作的很大一部分都由框架承擔了,在程序員看來,這個過程是透明的,用戶界面端的HTTP請求可以直接映射到后端的一個事件處理例程,JSF起到了承前啟后的作用。?

            5.?JSF優勢之四:程序員和網頁設計人員的分工

            在JSP中,程序員和網頁設計人員的工作有時候是互相交織、無法區分的。這是因為JSP頁面中摻入了網頁設計人員所不熟悉的一些JSP標簽,甚至是晦澀的Java代碼。要求網頁設計人員理解這些標簽和代碼是不現實的,不符合分工合作的原則。在JSF中,框架為網頁設計人員提供了一套標準的UI組件,在工具的支持下,可以通過拖放簡單地添加到網頁上,然后設置某些顯示屬性來滿足視覺要求。網頁設計人員不需要知道UI組件背后的復雜代碼,那是程序員的事,而程序員也不需要再處理任何與視覺相關的細節,程序員所做的只是給UI組件綁定類的屬性或方法。雖然程序員和網頁設計人員需要修改同一份文件,但他們各司其職,各得其所,互不干擾。程序員和網頁設計人員工作的明確劃分,是JSF在易用性方面邁出的一大步。

            6.?JSF優勢之五:請求處理生命周期的多階段劃分

            雖然都是建立在Servlet基礎之上,但JSF的生命周期要比JSP復雜得多。JSP的生命周期非常簡單,頁面被執行時,HTML標記立即被生成了,生命周期隨即結束。而一個完整的JSF請求-處理生命周期被精心規劃為6個階段,典型的JSF請求需要經歷所有階段,某些特殊的請求也可以跳過一些階段。階段的細分,顯然引入了更多的處理,但JSF框架會管理這一切,所以,程序員在獲得更多控制能力的同時,工作量并沒有增加。?

            ?7.?JSF優勢之六:伴隨工具而生存

            JSF帶來了Web編程的巨大變革,變革的強烈程度超出了很多工具廠商的預料,以至于現在可供JSF使用的工具非常缺乏。缺乏工具支持的JSF只會令人敬而遠之,因此,JSF在設計之初就為工具廠商預留了用武之地。在為數不多的JSF工具中,sun的Java?Studio?Creator是一個優秀的開發環境;Borland的Jbuilder在JSF1.1時曾經是非常好用的開發工具,可惜現在對JSF1.2的支持沒有跟上;Eclipse下JSF的插件很多,但真正支持所見即所得的JSF插件都是收費的,例如Bea的Workshop?for?JSF、Exadel的JSF?Studio等等;此外,Oracle和IBM也有JSF的開發工具。隨著工具的不斷涌現,用JSF開發Web程序將會越來越方便和快速。?

            ?8.?JSF優勢之七:全面的用戶自定義支持

            前面提到,JSF將極大地簡化Web程序的開發,作為一個相對復雜的框架,JSF是如何做到這點的呢?原來JSF為程序員提供了很多默認的組件和類,通常情況下,JSF的這些默認組件和類足以滿足Web開發的需要了。但是,考慮到在某些應用場合,框架的默認行為也許不符合業務的要求,JSF特別允許程序員編寫自己的組件和類,來滿足客戶的特殊需求。例如,程序員可以編寫自己的UI組件,甚至可以創建自己的EL解釋器,來支持非標準的EL表達語言。?

            9.?JSF優勢之八:Web開發的官方標準之一

            JSF的1.0版本發布于2004年2月份,當時是作為一項獨立的Web技術推出的。經過1.1版到現在最新的1.2版,短短的兩年多時間,JSF終于在2006年年中成為Java?EE?5的組成部分,上升為Web開發的官方標準之一。Java?EE?5最重要的使命就是簡化Java的開發,而JSF無疑為這一使命立下了汗馬功勞。在Web框架層出不窮甚至有些泛濫成災的今天,Sun以JSF來樹立標準,對Java的發展是有益的。Sun在Java領域的領袖地位不容動搖,對于Java程序員來說,始終追隨業界領袖的步伐,也許是避免技術落伍的最好方法。

            10.?結束語:該你了,JSF!

            考察某項技術的流行程度,google的關鍵字搜索不失為一種簡便易行的方法。為了便于說明,我們選擇目前最熱門的Struts與JSF進行比較。在google中分別輸入關鍵字“Struts”和“JSF”,看看google返回的網頁數量。令我們感興趣的不是網頁的絕對數量,而是JSF網頁數量與Struts網頁數量的比值。我們看到,對于英文網頁,這個比值是0.6,日文網頁是1.0,繁體中文網頁是0.8,而簡體中文網頁只有0.4。表1列出了具體的數據和比值。

            英文網頁數量(萬)?日文網頁數量(萬)?繁體網頁數量(萬)?簡體網頁數量(萬)

            JSF?Struts?JSF?Struts?JSF?Struts?JSF?Struts

            719?1170?145?140?10?13?59?138

            JSF?/?Struts?=?0.6JSF?/?Struts?=?1.0JSF?/?Struts?=?0.8JSF?/?Struts?=?0.4

            雖然這樣的比較方法不夠嚴謹,但0.4的比例從一個側面說明JSF在國內還沒有流行起來,用“方興未艾”四個字來描述JSF的這種狀況,是再合適不過了。由于歷史的原因,國內的軟件技術一直亦步亦趨地跟著國外跑,這不是我們希望的,但我們不得不承認,因此,從國外的情況來推論,我們有理由相信,JSF必將成為國內程序員追捧的對象。正如某位哲人說的那樣,JSF是早晨八、九點鐘的太陽,希望寄托在JSF身上。?

            11.?后記:不同的聲音

            客觀地說,JSF并非完美,業界對JSF的評價也褒貶不一。例如,由于JSF過于復雜,其學習曲線明顯長于其他框架如Struts等,這在一定程度上妨礙了JSF的推廣;此外,JSF的推出略顯倉促,1.0版在使用中發現很多bug,以至于sun匆忙發布的1.1版主要是為了修正這些bug;還有,在JSF1.2版之前,JSP和JSF的融合有嚴重的缺陷,這主要是由于二者不同的生命周期造成的,不過,1.2版在這方面作出了改進,現在,JSP和JSF可以在一個項目中相安無事了。?

            JSF的不足之處還有很多,比如UI組件不夠豐富、具體實現的可選擇余地過窄、使用JSF開發的實際項目不多、sun的參考實現還存在諸多bug、短期內缺乏工具支持等等,尤其是在國內,JSF的中文文檔和書籍相當缺乏。但是,不管怎樣,這些都是JSF成長道路上必須經歷的磨難,我相信,Sun會努力的。




          EJB的理想
          yongbing 轉貼???更新:2007-01-03 16:50:45??版本: 1.0 ??

          摘要:
          EJB是一種企業應用技術,旨在建立一個企業應用開發框架,但從其誕生之日起,質疑之聲一直不斷。EJB是企業應用框架的先驅,在企業應用框架的方法論上有獨到的見解,雖然存在不少缺陷,但仍不失為企業應用框架的理想。

          1.?備受爭議的EJB

            EJB也許是Java領域里中最受爭議的技術了。有人說EJB是最偉大的發明,也有人說EJB完全是多此一舉;當一些人陶醉于EJB的深奧理論時,另外一些人卻正被EJB的繁瑣復雜所折磨;嘗到EJB甜頭的人,在EJB的每個新版本中,都能發現盼望已久的驚喜,而被EJB拒之門外的人,則隨著EJB的升級,愈發對EJB敬而遠之了。

            EJB的全稱是Enterprise?JavaBeans,JavaBeans很普通,不過Enterprise就不那么簡單了。什么技術,一旦被冠以Enterprise的名頭,就像男人走入婚姻殿堂一樣,身上的責任與單身漢不可同日而語了。從定義上看,JavaBeans只是J2SE平臺上的一個組件架構,包含一些業務邏輯,并且可以被重用。

            EJB不同,作為企業級的JavaBeans,Sun對EJB的定位要遠遠高于JavaBeans,所以EJB的目標也比JavaBeans要遠大得多,除了作為一個包含業務邏輯的可重用組件外,EJB更被賦予了諸如“可移植”、“安全”、“可伸縮”、“交易性”等特征。

            所有這些EJB必須具備的特征,其實正是企業應用所要求的。這也是Enterprise一詞所代表的技術上的含義。企業應用不同于普通應用,企業應用是大規模的、高復雜度的和關鍵的,它所面臨的挑戰,要比普通應用艱巨得多。比如,企業應用對可移植性的要求非常高,這是因為,企業都不愿意將自己的未來綁定到某個供應商的身上,除非是不得已而為之;又比如,安全性對企業應用至關重要,誰能使用什么功能、哪些數據哪些人可以看到,都有嚴格的限制;更不用說的是企業應用的可伸縮性了,當業務規模變大時,你希望全盤推翻舊系統,采購一批嶄新的軟件和硬件,對IT系統來個徹底的革命嗎?增加一臺服務器就能應付更多的客戶,我想這是頭腦正常的企業家都希望的。

            企業應用的需求,就是EJB的目標。用EJB開發的應用,完全符合企業應用的特征。EJB是一個規范,只要符合這個規范,EJB可以在不同的操作系統、不同的應用服務器中無縫地移植;EJB允許開發者在EJB部署描述文件中進行方法級的、基于角色的安全性配置,以統一的方式保護企業應用和數據的安全性;只要你愿意,EJB應用可以全部部署在一臺單獨的服務器上,也可以任何組合方式分布在一組服務器群中,滿足你擴大規模和均衡負載的要求;如果你想保持事務的完整性,那么,EJB的事務管理是一個可靠的、穩健的解決方案。

            這就是EJB,一個企業應用的集大成者,多種技術的濃縮精華,全能的框架和基礎結構。可就是這樣一個將企業應用的開發簡化到了前所未有之程度的技術,卻成為許多人口誅筆伐的對象。復雜、難以使用、性能低下、繁瑣等等,從1998年EJB誕生之日起,各種各樣的惡名就伴隨左右,直到八年后的今天,當EJB迎來它的第三次大變臉時,質疑之聲依然不絕于耳。EJB真的那么糟糕嗎?

            2.?EJB是企業應用的先驅

            筆者接觸第一個企業應用,是在1997年。那時PowerBuilder風頭正勁,不過,多數人使用PowerBuilder,是因為它的數據窗口。當時筆者在一個項目中遇到一個難題,那就是如何把一臺服務器上的應用一分為二,跑在兩臺服務器上,以提高性能。這是典型的分布式應用,雖然不是一個完整意義上的企業應用,不過,因為應用中需要用到分布式的概念,多少也算和企業應用沾上邊了。

            PowerBuilder其實是個非常不錯的開發工具,在1997年的時候,已經提出了分布式應用的概念,并且付諸實施了。在PowerBuilder中,一個組件可以有一個稱為代理的對象,這個對象可以運行在與組件不同的機器上,其他組件通過代理可以訪問該組件的功能。

            這是一個很初級的分布式應用框架,不過,那時已經給了筆者很大的震動。我試著編了一個實驗性質的程序,當我在一臺機器上按下一個按鈕時,另外一臺機器上赫然彈出一個預期中的對話框,著實讓我大吃一驚。沒有任何Socket編程,也不需要關心實際的應用跑在哪臺機器上,PowerBuilder讓我首次見識了分布式應用框架的巨大威力。

            PowerBuilder解決了分布的問題,但安全性和事務控制,仍然需要程序員自己想辦法。十個程序員可以有十種解決方案,每種都不同,而每種都可能含有未經發現的缺陷。在EJB之前,企業應用的開發沒有規范可循,每個公司都有自己的一套方案,盡管每個公司都對自己的方案充滿信心,但其實這些未經大量應用考驗的方案,都有著這樣那樣的不足或局限。

            J2EE是第一個為業界所廣為接受的完整的企業應用框架,而EJB在其中扮演重要角色。在J2EE框架的支持下,運行在EJB容器中的EJB,完全符合企業應用關于分布、移植、安全和交易的要求。這對于企業應用的開發者來說,意義非同尋常。首先,現在大家可以在一個公共的平臺技術上構建自己的企業應用,不必絞盡腦汁“發明”自己的“輪子”,從而節省大量無謂的、重復性的技術和時間投入;其次,一個公開的平臺,讓大量的企業應用開發者有了共同語言,可以相互交流平臺的使用經驗和教訓,這樣,隨著平臺之上企業應用的不斷增加,平臺的優劣得失一覽無遺,有利于平臺的改進和發展。

            這就是EJB為企業應用作出的貢獻。在EJB之前,多數人不知企業應用為何物,或者雖然有企業應用的模糊概念,但要編寫一個企業應用,談何容易。不同的操作系統、不同的開發語言、不同的網絡環境、不同的應用終端,開發一個企業應用,程序員面臨著兩難的抉擇:要么限定應用的軟硬件平臺,或者犧牲應用的安全性、分布性或交易性,開發一個“偽”企業應用;要么下決心開發一個真正的企業應用,然后累死自己。

            3.?EJB是一種思想

            EJB讓程序員編寫真正意義上的企業應用而不必累死,應該說,EJB已經是企業應用開發領域的一大進步,讓企業應用和普通應用的開發差距縮短到了前所未有的程度,可是,為什么還有很多人抱怨EJB過于復雜呢?EJB的復雜性其實是個偽命題。所謂復雜,一定是相對的。如果和普通應用相比,EJB當然要復雜很多,因為人們對于普通應用沒有企業應用那么苛刻的要求。但是,如果將EJB之前和EJB之后的企業應用開發的難度相比,相信人們就會對EJB贊譽有加了。舉個不準確的例子,假如EJB之前,企業應用的難度是普通應用的10倍,那么,EJB之后,這個比例縮小到了5倍,批評EJB復雜者,只看到了還剩下的5倍復雜度,卻沒有看到EJB所減去的5倍復雜度。

            關于EJB過于復雜的論斷,還來自于與其競爭技術的比較。例如,Spring就是一個聲稱比EJB更簡單的、輕量級的企業應用框架。Spring確實簡單,很多人喜歡Spring,也正是因為Spring簡單。可是,Spring的支持者們,忽略了一個基本的事實,那就是Spring的功能要比EJB弱,也就是說,Spring是通過放棄某些不常用的功能來達到簡化目的的。Spring好比一輛沒有安全氣囊的車,盡管依然可以拉客跑運輸,但一旦發生碰撞事故,也許司機會陪上性命。沒有安全氣囊的Spring,在制造上當然要比有安全氣囊的EJB簡單,而且輕便,跑得快,不過,Spring始終不適合投入正式營運。這就是為什么不推薦在大型企業應用上采用Spring的原因。沒有完善的事務處理,不能提供7X24小時的服務,Spring邁不過關鍵企業應用的門檻。

            與Spring形影不離的是Java對象持久化的“紅人”Hibernate。Hibernate的矛頭直指EJB的Entity?Bean。Entity?Bean,尤其是它的持久化技術,是最為程序員所詬病的,成為EJB揮之不去的陰影,并最終促成了Hibernate的輝煌。Hibernate其實并不精深,在技術上也沒有太多值得稱道的創新,但它的文檔非常優秀。我知道很多程序員就是被Hibernate的文檔所吸引的,他們只學過一些SQL初步,沒有系統的關系數據庫理論知識,Hibernate關于數據庫表間關系的論述,深入淺出,十分精彩,讓他們在對關系數據庫的理解上有了迅猛突破的同時,Hibernate輕易的俘虜了他們的心。

            Hibernate的成功,反襯了EJB在持久化方面的失敗,但在我看來,這并不影響EJB的偉大。與其說EJB是一種技術,不如說EJB的是一種思想更恰當,而不論Hibernate還是Spring,只不過是一種工具,他們只是跟在EJB后面,發現了EJB的某些不足,然后有針對性地加以改進,以迎合普通程序員對于“技術快餐”的需求。

            他們既沒有從形形色色的企業應用中,抽象出隱藏在不同表現后面的本質特征,也沒有創造性地用Stateless?Session?Bean和Stateful?Session?Bean來描述千變萬化的現實世界。工具只是工具,不出兩年就會有新的后起之秀,取而代之,但思想的光輝將長久地照亮技術的未來。EJB是一種思想,更是一種理想,盡管理想和現實總是存在差距,但這不能成為我們放棄EJB的理由。一種滿足企業應用分布性、擴展性、安全性和交易性要求的、方便使用的框架技術,既是EJB的理想,也是廣大程序員的理想。
          當前流行的J2EE?WEB應用架構分析
          xuyy_cn 原創???更新:2006-09-14 13:04:12??版本: 1.0 ??

          1.?架構概述?


          J2EE體系包括java?server?pages(JSP)?,java?SERVLET,?enterprise?bean,WEB?service等技術。這些技術的出現給電子商務時代的WEB應用程序的開發提供了一個非常有競爭力的選擇。怎樣把這些技術組合起來形成一個適應項目需要的穩定架構是項目開發過程中一個非常重要的步驟。完成這個步驟可以形成一個主要里程碑基線。形成這個基線有很多好處:?


          各種因數初步確定?

          為了形成架構基線,架構設計師要對平臺(體系)中的技術進行篩選,各種利弊的權衡。往往架構設計師在這個過程中要閱讀大量的技術資料,聽取項目組成員的建議,考慮領域專家的需求,考慮贊助商成本(包括開發成本和運行維護成本)限額。一旦架構設計經過評審,這些因數初步地就有了在整個項目過程中的對項目起多大作用的定位。?

          定向技術培訓?

          一旦架構師設計的架構得到了批準形成了基線,項目開發和運行所采用的技術基本確定下來了。眾多的項目經理都會對預備項目組成員的技術功底感到擔心;他們需要培訓部門提供培訓,但就架構師面對的技術海洋,項目經理根本就提不出明確的技術培訓需求。怎不能夠對體系中所有技術都進行培訓吧!有了架構里程碑基線,項目經理能確定這個項目開發會采用什么技術,這是提出培訓需求應該是最精確的。不過在實際項目開發中,技術培訓可以在基線確定之前與架構設計并發進行。?

          角色分工?

          有了一個好的架構藍圖,我們就能準確劃分工作。如網頁設計,JSP?標簽處理類設計,SERVLET?設計,session?bean設計,還有各種實現。這些任務在架構藍圖上都可以清晰地標出位置,使得項目組成員能很好地定位自己的任務。一個好的架構藍圖同時也能規范化任務,能很好地把任務劃分為幾類,在同一類中的任務的工作量和性質相同或相似。這樣工作量估計起來有一個非常好的基礎。?

          運行維護?

          前面說過各個任務在架構圖上都有比較好的定位。任何人能借助它很快地熟悉整個項目的運行情況,錯誤出現時能比較快速地定位錯誤點。另外,有了清晰的架構圖,項目版本管理也有很好的版本樹軀干。?

          擴展性?

          架構猶如一顆參天大樹的軀干,只要軀干根系牢,樹干粗,長一些旁支,加一些樹葉輕而易舉無疑。同樣,有一個穩定的經得起考驗的架構,增加一兩個業務組件是非常快速和容易的。?

          大家都知道這些好處,一心想形成一個這樣的J2EE應用程序架構(就像在windows平臺中的MFC)。在這個路程中經歷了兩個大的階段:?


          1.1.?模型1?


          模型1其實不是一個什么穩定架構,甚至談不上形成了架構。模型1的基礎是JSP文件。它從HTTP的請求中提取參數,調用相應的業務邏輯,處理HTTP會話,最后生成HTTP文檔。一系列這樣的JSP文件形成一個完整的模型1應用,當然可能會有其他輔助類或文件。早期的ASP?和?PHP?技術就屬于這個情況。?


          總的看來,這個模型的好處是簡單,但是它把業務邏輯和表現混在一塊,對大應用來說,這個缺點是令人容忍不了的。?


          1.2.?模型2?


          在經過一番實踐,并廣泛借鑒和總結經驗教訓之后,J2EE應用程序終于迎來了MVC(模型-視圖-控制)模式。MVC模式并不是J2EE行業人士標新立異的,所以前面我談到廣發借鑒。MVC的核心就是做到三層甚至多層的松散耦合。這對基于組件的,所覆蓋的技術不斷膨脹的J2EE體系來說真是福音和救星。?


          它在瀏覽器(本文對客戶代理都稱瀏覽器)和JSP或SERVLET之間插入一個控制組件。這個控制組件集中了處理瀏覽器發過來的HTTP請求的分發邏輯,也就是說,它會根據HTTP請求的URL,輸入參數,和目前應用的內部狀態,把請求分發給相應的WEB?層的JSP?或SERVLET。另外它也負責選擇下一個視圖(在J2EE中,JSP,SERVLET會生成回給瀏覽器的html從而形成視圖)。集中的控制組件也有利于安全驗證,日志紀錄,有時也封裝請求數據給下面的WEB?tier層。這一套邏輯的實現形成了一個像MFC的應用框架,位置如圖:?


          1.3.?多層應用?


          下圖為J2EE體系中典型的多層應用模型。?


          Client?tier客戶層?

          一般為瀏覽器或其他應用。客戶層普遍地支持HTTP協議,也稱客戶代理。?

          WEB?tier?WEB應用層?

          在J2EE中,這一層由WEB?容器運行,它包括JSP,?SERVLET等WEB部件。?

          EJB?tier?企業組件層?

          企業組件層由EJB容器運行,支持EJB,?JMS,?JTA?等服務和技術。?

          EIS?tier?企業信息系統層?

          企業信息系統包含企業內傳統信息系統如財務,CRM等,特點是有數據庫系統的支持。?


          應用框架目前主要集中在WEB層,旨在規范這一層軟件的開發。其實企業組件層也可以實現這個模型,但目前主要以設計模式的形式存在。而且有些框架可以擴充,有了企業組件層組件的參與,框架會顯得更緊湊,更自然,效率會更高。?


          2.?候選方案?


          目前,實現模型2的框架也在不斷的涌現,下面列出比較有名的框架。?


          2.1.?Apache?Struts?


          Struts是一個免費的開源的WEB層的應用框架,apache軟件基金致力于struts的開發。Struts具是高可配置的性,和有一個不斷增長的特性列表。一個前端控制組件,一系列動作類,動作映射,處理XML的實用工具類,服務器端java?bean?的自動填充,支持驗證的WEB?表單,國際化支持,生成HTML,實現表現邏輯和模版組成了struts的靈魂。?


          2.1.1.?Struts和MVC?


          模型2的目的和MVC的目的是一樣的,所以模型2基本可以和MVC等同起來。下圖體現了Struts的運作機理:?


          2.1.1.1.?控制?


          如圖所示,它的主要部件是一個通用的控制組件。這個控制組件提供了處理所有發送到Struts?的HTTP請求的入口點。它截取和分發這些請求到相應的動作類(這些動作類都是Action類的子類)。另外控制組件也負責用相應的請求參數填充?From?bean,并傳給動作類。動作類實現核心商業邏輯,它可以通過訪問java?bean?或調用EJB。最后動作類把控制權傳給后續的JSP?文件,后者生成視圖。所有這些控制邏輯利用一個叫struts-config.xml文件來配置。?


          2.1.1.2.?模型?


          模型以一個或幾個java?bean的形式存在。這些bean分為三種:?


          Form?beans(表單Beans)?

          它保存了HTTP?post請求傳來的數據,在Struts里,所有的Form?beans都是?ActionFrom?類的子類。?

          業務邏輯beans?

          專門用來處理業務邏輯。?

          系統狀態beans?

          它保存了跨越多個HTTP?請求的單個客戶的會話信息,還有系統狀態。?

          2.1.1.3.?視圖?


          控制組件續傳HTTP請求給實現了視圖的JSP文件。JSP能訪問beans?并生成結果文檔反饋到客戶。Struts提供JSP?標簽庫:?Html,Bean,Logic,Template等來達到這個目的,并有利于分開表現邏輯和程序邏輯。?


          2.1.2.?Struts的細節分析?


          2.1.2.1.?視圖-控制-模型?


          用戶發出一個*.do的HTTP請求,控制組件接收到這個請求后,查找針對這個請求的動作映射,再檢查是否曾創建過相應的動作對象(action實例),如果沒有則調用actionmapping生成一個動作對象,控制組件會保存這個動作對象供以后使用。接著調用actionmapping的方法得到actionForm對象。之后把actionForm作為參數傳給動作對象的perform方法,這個方法結束之后會返回給控制組件一個?actionforward對象。控制組件接著從這個對象中獲取下一個視圖的路徑和重定向屬性。如果為重定向則調用HTTPSERVLETREPONSE的方法來顯示下一個視圖,否則相繼調用requestdispatcher,?SERVLETcontext的方法續傳HTTP請求到下一個視圖。?


          當動作對象運行perform方法時,可能出現錯誤信息。動作對象可以保存這些錯誤信息到一個error對象中,接著調用自身的saveerrors方法把這個錯誤保存到request對象的屬性中。接著動作對象調用actionmapping對象的getInput方法從動作映射中獲取input參數,也就是產生輸入的視圖,并以這個input為參數生成一個actionforward對象返回。這個input參數的JSP中一般有HTTP:errors定制標簽讀取這些錯誤信息并顯示在頁面上。?


          2.1.2.2.?模型到視圖?


          模型到視圖指視圖在顯示之前裝載系統數據到視圖的過程。系統數據一般為模型內java?bean的信息。示意圖表現了由控制組件forward過來的有html:form定制標簽的JSP?的處理邏輯。?


          html:form定制標簽處理對象從application?scope(通過查詢SERVLETCONTEXT對象的屬性來實現)獲取先前由控制組件actionSERVLET放在那里的動作映射等對象,由html:form?的action屬性查得actionform名字、類型和范圍等信息,在相應的范圍內查找actionform,如果有則利用它的信息填充html?form表單[實際填充動作在嵌套的html:text等定制標簽的處理對象中]。否則在相應范圍內創建一個actionform?對象。?


          2.1.3.?優缺點?


          優點:?


          一些開發商開始采用并推廣這個框架?

          作為開源項目,有很多先進的實現思想?

          對大型的應用支持的較好?

          有集中的網頁導航定義?

          缺點:?


          不是業屆標準?

          對開發工具的支持不夠?

          復雜的taglib,需要比較長的時間來掌握?

          html?form?和?actionform的搭配比較封閉,但這也是它的精華所在。?

          修改建議?

          把actionform屬性的設置器和訪問器修改成讀取或生成xml文檔的方法,然后?html?form和actionform之間用xml文檔進行數據交換,使之松散耦合,適應數據結構易變化的應用。?


          2.2.?JATO?


          JATO應用程序框架是iPlanet?應用程序框架的舊名。它是一個成熟的、強大的,基于J2EE標準的面向于開發WEB應用程序的應用框架。結合了顯示字段、應用程序事件、組件層次和以頁面為中心的開發方法、以及MVC和服務到工作者service-to-workers的設計模式等概念。JATO可適用于中、大、超大規模的WEB應用。但是它也不是一個企業層的應用框架,也就是說它不會直接提供創建EJB,?WEB?services等企業層組件的方法,但用它可以構造出訪問企業層組件的客戶應用。?


          這個框架功能主要有三部分組成:?


          iPlanet應用框架核心;?

          iPlanet應用框架組件;?

          iPlanet應用框架擴展。?

          應用框架核心定義了基本接口、對象協議、簡單組件,以及iPlanet應用框架程序的最小核心。包括視圖簡單組件、模型簡單組件、請求分發組件和可重用命令對象。iPlanet應用框架組件利用框架核心定義的基本接口、協議和組件向開發者提供高層的重用組件,這些組件既有與特定視覺效果無關的水平組件,同時也有適應特定實用環境、提高可用性而特意提供的垂直型組件。框架擴展實現了用框架相容的方法訪問非J2EE環境的方法。通常情況下,擴展被框架應用程序用來無縫訪問J2EE容器特定功能。JATO平臺棧圖很清楚地表達了這個情況。?


          JATO最大的威力在:對于快速開發用戶,你能利用框架組件和擴展提高生產率,對于要求更大靈活性的用戶,你能實現框架核心提供的接口來保持應用的框架兼容性。?

          此圖表示實現一個JATO應用程序,可以簡單地實現控制組件module1Servlet,視圖組件ListCustomersViewBean和模型組件CustomersModuleImpl,以及一個給客戶代理顯示界面的ListCustomers.jsp文件。并清楚地表明這些組件與JATO框架組件的繼承關系。?


          JATO標簽庫提供了VIEW對象與JSP文件的接口。庫中標簽處理程序負責實現VIEW對象和JSP產生地客戶端文檔的信息同步和交換。這個圖清楚地表達了這種對應關系?


          2.2.1.?MVC分析?


          前端控制組件接收用戶發來的任何請求,這個可在WEB.xml中指定請求分發組件負責視圖管理和導航,和前端控制組件封裝在ApplicationSERVLETBase一起實現。應用程序開發者需要為每一個子系統(人力資源,財務,CRM等)實現一個此類的繼承。?


          請求分發組件分發請求給工作者,工作者實現了command接口。應用開發者可以實現這個接口。JATO提供了一個缺省實現:DefaultRequestHandingCommand,這個實現會把請求傳給視圖組件的特定事件。?


          組合視圖是指視圖組件在顯示給用戶時的層次關系:根視圖是一個ViewBean類的對象字段是一個DisplayField類的對象,容器視圖是一個ContainerView類的對象。視圖組件類的層次關系如下圖:?


          2.2.2.?優缺點分析?


          優點:?


          這種框架的適應范圍大,即提供了底層接口,也有立即可用的組件?

          具有與客戶端RAD開發工具相似的開發概念如頁為中心(等同于VB的FORM),事件處理等.?

          對大型的應用支持較好?

          缺點:?


          不是業屆標準?

          目前還沒有開發工具的支持(然JATO已經為工具支持做好了準備)?

          沒有定義網頁導航,開發者在視圖中自己指定具體的導航URL?

          修改建議?

          把眾多的VIEW/MODEL對應修改成xml文檔傳遞數據,加上集中的網頁導航定義?


          2.3.?JSF(JavaServer?Faces)?


          JSF是一個包括SUN在內的專家組正在定義的開發WEB應用用戶界面的框架,JSF?技術包括:?


          一組API,它實現UI了組件,管理組件的狀態,處理事件,輸入校驗,定義頁面導航,支持國際化和訪問;?

          一個JSP定制標簽庫實現與JSP的接口。?

          JSF非常簡單,是一個定義良好的編程模型。利用這個技術,開發者通過在頁面內組合可重用的UI組件,在把這些組件和應用的數據源相連,路由客戶產生的事件到服務器端的事件處理器進行編程。JSP處理了所有幕后的復雜工作,使得開發者把關注重點放在應用代碼上。?


          2.3.1.?STRUTS、JATO和JSF比較?


          它們之間有部分重疊,但重點不一樣。?


          STRUTS和JATO都提供了一個MVC式的應用模型,而JSF只在用戶界面上提供編程接口。這意味著前兩者涉及的范圍比后者廣。JSF可以成為前兩者在UI開發的部分。?

          JSF的規范的發布版將在?2002年底發布,實現可能要比這個時間晚些。另外將會有工具支持這個框架的應用開發。?

          2.4.?WAF?


          WAF是WEB?APPLICATION?FRAMWORK的簡稱,是SUN藍皮書例子程序中提出的應用框架。它實現了?MVC和其他良好的設計模式。?


          2.4.1.?細節分析?



          2.4.2.?視圖-控制-模型?


          如圖所示,開發人員編寫的兩個xml配置文件定義了WAF的運作參數。Screendefinition.xml定義了一系列的屏幕(screen)。Mapping.xml則定義了某個動作之后應該顯示的屏幕,但沒有指定屏幕到哪里拿數據。?


          用戶發出一個HTTP請求(*.screen),由TemplateSERVLET屏幕前端控制組件接收,它提取請求信息,設置request對象CurrentScreen屬性,再把請求發到模版JSP。模版JSP收到請求后,JSP中的Template標簽察看這個當前屏幕,并從屏幕定義文件(Screendefinition.xml)中獲取這個屏幕的具體參數,再生成html返回給客戶。?


          假設返回給客戶的html中包括了html表單,用戶在輸入一定數據之后提交,發出一個HTTP請求(*.do)。這個請求被MainSERVLET接收,它提取請求信息,察看動作映射文件(mapping.xml),設置處理這個請求的動作對象(HTTPAction對象),交給requestprosessor對象處理。Requestprosessor對象調用動作對象完成任務,如果需要進一步處理,requestprosessor對象會調用WEBclientcontroler對象的事件處理機制。MainSERVLET在處理完請求之后,從屏幕流管理對象那里得到下一個屏幕,并把請求傳給這個屏幕的JSP文件。?


          值得一提的是WEBclientcontroler事件處理機制最終把HTTP請求的數據傳到了EJBAction對象那里處理。這樣HTTPAction對象和EJBAction對象形成了兩級處理機制,前一級與request對象緊密相關,把數據封裝起來形成一個Event對象,再傳給了EJBAction對象,后者與Request對象無關。這個方式可以形成一個session級別的數據處理機制。下圖顯示了這個方法。HTTPAction1對象處理一個請求,并把數據放到一個狀態SessionBean內,HTTPAction2也如此,當HTTPAction3接收到HTTP請求之后,把控制傳給EJBAction,?后者獲取狀態SessionBean數據,處理請求,成功后清控狀態SessionBean的內容。這個機制非常適應多個輸入頁面才能滿足一個業務的輸入數據的情況(比如購物車)。?


          2.4.3.?優缺點分析?


          優點?


          屏幕導航定義明確?

          為框架的擴展提供了一個空間?

          缺點?


          源碼比較亂,穩定性和可靠性沒人驗證。?

          只是一個框架軀干,沒有正式的model層,視圖的概念不強?

          沒有模型到視圖的定義?

          修改意見?

          只有一個框架軀干,正為實現自己的應用框架提供了靈活性。沒有僵化的視圖概念,提供了在網頁輸入到模型的擴充接口,比如插入XML數據交換。?
          Java?EE?常見性能問題解決手冊
          xuyy_cn 轉貼???更新:2006-09-14 13:25:57??版本: 1.0 ??

          這篇文章,是PRO?JAVA?EE?5?Performance?Management?and?Optimization?的一個章節,作者Steven?Haines分享了他在調優企業級JAVA應用時所遇到的常見問題。

            Java?EE(Java企業開發平臺)應用程序,無論應用程序服務器如何部署,所面對的一系列問題大致相同。作為一個JAVAEE問題解決專家,我曾經面對過眾多的環境同時也寫了不少常見問題的觀察報告。在這方面,我覺得我很象一個汽車修理工人:你告訴修理工人發動機有聲音,他就會詢問你一系列的問題,幫你回憶發動機運行的情形。從這些信息中,他尋找到可能引起問題的原因。

            眾多解決問題的方法思路基本相同,第一天我同要解決問題的客戶接觸,接觸的時候,我會尋找已經出現的問題以及造成的負面的影響。了解應用程序的體系結構和問題表現出的癥狀,這些工作很夠很大程度上提高我解決問題的幾率。在這一節,我分享我在這個領域遇過的常見問題和他們的癥狀。希望這篇文章能成為你JAVAEE的故障檢測手冊。

            內存溢出錯誤

            最常見的折磨著企業級應用程序的錯誤是讓人恐懼的outofmemoryError(內存溢出錯誤)

            這個錯誤引起下面這些典型的癥狀:

            ----應用服務器崩潰?
            ----性能下降?
            ----一個看起來好像無法結束的死循環在重復不斷的執行垃圾收集,它會導致程序停止運行,并且經常導致應用服務器崩潰
            不管癥狀是什么,如果你想讓程序恢復正常運行,你一般都需要重新啟動應用服務器。

            引發out-of-memory?錯誤的原因

            在你打算解決out-of-memory?錯誤之前,首先了解為什么會引發這個錯誤對你有很大的幫助。如果JVM里運行的程序,?它的內存堆和持久存儲區域的都滿了,這個時候程序還想創建對象實例的話,垃圾收集器就會啟動,試圖釋放足夠的內存來創建這個對象。這個時候如果垃圾收集器沒有能力釋放出足夠的內存,它就會拋出OutOfMemoryError內存溢出錯誤。

            Out-of-memory錯誤一般是JAVA內存泄漏引起的。回憶上面所討論的內容,內存泄漏的原因是一個對象雖然不被使用了,但是依然還有對象引用他。當一個對象不再被使用時,但是依然有一個或多個對象引用這個對象,因此垃圾收集器就不會釋放它所占據的內存。這塊內存就被占用了,堆中也就少了塊可用的空間。在WEB?REQUESTS中這種類型的的內存泄漏很典型,一兩個內存對象的泄漏可能不會導致程序服務器的崩潰,但是10000或者20000個就可能會導致這個惡果。而且,大多數這些泄漏的對象并不是象DOUBLE或者INTEGER這樣的簡單對象,而可能是存在于堆中一系列相關的對象。例如,你可能在不經意間引用了一個Person對象,但是這個對象包含一個Profile對象,此對象還包含了許多擁有一系列數據的PerformanceReview對象。這樣不只是丟失了那個Person對象所占據的100?bytes的內存,你丟失了這一系列相關對象所占據的內存空間,可能是高達500KB甚至更多。

            為了尋找這個問題的真正根源,你需要判斷是內存泄漏還是以OutOfMemoryError形式出現的其他一些故障。我使用以下2種方法來判斷:

            ----深入分析內存數據?
            ----觀察堆的增長方式
            不同JVM(JAVA虛擬機)的調整程序的運作方式是不相同的,例如SUN和IBM的JVM,但都有相同的的地方。

            SUN?JVM的內存管理方式

            SUN的JVM是類似人類家族,也就是在一個地方創建對象,在它長期占據空間之前給它多次死亡的機會。

            SUN?JVM會劃分為:

            1?年輕的一代(Young?generation),包括EDEN和2個幸存者空間(出發地和目的地the?From?space?and?the?To?space)?
            2?老一代(Old?generation)?
            3?永久的一代(Permanent?generation)
          圖1?解釋了SUN?堆的家族和空間的詳細分類

          對象在EDEN出生就是被創建,當EDEN滿了的時候,垃圾收集器就把所有在EDEN中的對象掃描一次,把所有有效的對象拷貝到第一個幸存者空間,同時把無效的對象所占用的空間釋放。當EDEN再次變滿了的時候,就啟動移動程序把EDEN中有效的對象拷貝到第二個幸存者空間,同時,也將第一個幸存者空間中的有效對象拷貝到第二個幸存者空間。如果填充到第二個生存者空間中的有效對象被第一個生存者空間或EDEN中的對象引用,那么這些對象就是長期存在的(也就是說,他們被拷貝到老一代)。若垃圾收集器依據這種小幅度的調整收集(minor?collection)不能找出足夠的空間,就是象這樣的拷貝收集(copy?collection),就運行大幅度的收集,就是讓所有的東西停止(stop-the-world?collection)。運行這個大幅度的調整收集時,垃圾收集器就停止所有在堆中運行的線程并執行清除動作(mark-and-sweep?collection),把新一代空間釋放空并準備重啟程序。

            圖2和圖3展示的是了小幅度收集如何運行

          圖2。對象在EDEN被創建一直到這個空間變滿。

          圖3。處理的順序十分重要:垃圾收集器首先掃描EDEN和生存者空間,這就保證了占據空間的對象有足夠的機會證明自己是有效的。
          圖4展示了一個小幅度調整是如何運行的

          圖4:當垃圾收集器釋放所有的無效的對象并把有效的對象移動到一個更緊湊整齊的新空間,它將EDEN和生存者空間清空。

            以上就是SUN實現的垃圾收集器機制,你可以看出在老一代中的對象會被大幅度調整器收集清除。長生命周期的對象的清除花費的代價很高,因此如果你希望生命周期短的對象在占據空間前及時的死亡,就需要一個主垃圾收集器去回收他們的內存。

            上面所講解的東西是為了更好的幫助我們識別出內存泄漏。當JAVA中的一個對象包含了一個并不想要的一個指向其他對象的引用的時候,內存就會泄漏,這個引用阻止了垃圾收集器去回收它所占據的內存。采用這種機制的SUN?虛擬機,對象不會被丟棄,而是利用自己特有的方法把他們從樂園和幸存者空間移動到老一代地區。因此,在一個基于多用戶的WEB環境,如果許多請求造成了泄漏,你就會發現老一代的增長。

            圖5顯示了那些潛在可能造成泄漏的對象:主收集器收集后遺留下來占據空間的對象會越來越多。不是所有的占據空間的對象都造成內存泄漏,但是造成內存泄漏的對象最終都占據者空間。如果內存泄漏的確存在,這些造成泄漏的對象就會不斷的占據空間,直至造成內存溢出。

            因此,我們需要去跟蹤垃圾收集器在處理老一代中的運行:每次垃圾收集器大幅度收集運行時,有多少內存被釋放?老一代內容是不是按一定的原理來增長?

           圖5。陰影表示在經過大幅度的收集后幸存下來的對象,這些對象是潛在可能引發內存泄漏的對象

            一部分這些相關的信息是可以通過跟蹤API得到,更詳細的信息通過詳細的垃圾收集器的日志也可以看到。和所有的跟蹤技術一樣,日值記錄詳細的程度影響著JVM的性能,你想得到的信息越詳細,付出的代價也就越高。為了能夠判斷內存是否泄漏,我使用了能夠顯示輩分之間所有的不同的較權威的技術來顯示他們的區別,并以此來得到結果。SUN?的日志報告提供的信息比這個詳細的程度超過5%,我的很多客戶都一直使用那些設置來保證他們管理和調整垃圾收集器。下面的這個設置能夠給你提供足夠的分析數據:

            ?verbose:gc??xloggc:gc.log??XX:+PrintGCDetails??XX:+PrintGCTimeStamps

            明確發現在整個堆中存在有潛在可能泄漏內存的情況,用老一代增長的速率才比較有說服力。切記調查不能決定些什么:為了能夠最終確定你內存泄漏,你需要離線在內存模擬器中運行你的應用程序。

          IBM?JVM內存管理模式

            IBM的JVM的機制有一點不同。它不是運行在一個巨大的繼承HEAP中,它僅在一個單一的地區維護了所有的對象同時隨著堆的增長來釋放內存。這個堆是這樣運行的:在一開始運行的時候,它會很小,隨著對象實例不斷的填充,在需要執行垃圾收集的地方清除掉無效的對象同時把所有有效的對象緊湊的放置到堆的底部。因此你可能猜測到了,如果想尋找可能發生的內存泄漏應該觀察堆中所有的動作,堆的使用率是在提高?

            如何分析內存泄漏

            內存泄漏非常難確定,如果你能夠確定是請求導致的,那你的工作就非常簡單了。把你的程序放入到運行環境中,并在內存模擬器中運行,按下面的步驟來:

            1.?在內存模擬器中運行你的應用程序?
            2.?執行使用方案(制造請求)以便讓程序在內存中裝載請求所需要的所有的對象,這可以為以后詳細的分析排除不必要的干擾?
            3.?在執行使用方案前對堆進行拍照以便捕獲其中所有運行的對象。?
            4.?再次運行使用方案。?
            5.?再次拍照,來捕獲使用方案運行之后堆中所有對象的狀態。?
            6.?比較這2個快照,找出執行使用方案后本不應該出現在堆中的對象。
            這個時候,你需要去和開發者交流,告訴他你所碰到的棘手的請求,他們可以判斷究竟是對象泄漏還是為了某個目的特地讓對象保留下來的。如果執行完后并沒有發現內存泄漏的情況,我一般會轉到步驟4再進行多次類似的跟蹤。比如,我可能會將我的請求反復運行17次,希望我的泄漏分析能夠得到17個情況(或更多)。這個方法不一定總有用,但如果是因為請求引起的對象泄漏的話,就會有很大的幫助。

            如果你無法明確的判斷泄漏是因為請求引發的,你有2個選擇:

            1.?模擬每一個被懷疑的請求直至發現內存泄漏?
            2.?存配置一個內存性能跟蹤工具
            第一個選項在小應用程序中是確實可用的或者你非常走運的解決了問題,但對大型應用程序不太有用。如果你有跟蹤工具的話第二個選擇是比較有用的。這些工具利用字節流工具跟蹤對象的創建和銷毀的數量,他們可以報告特定類中的對象的數量狀態,例如把Collections類作為特定的請求。例如,一個跟蹤工具可以跟蹤/action/login.do請求,并在它完成后將其中的100個對象放入HASHMAP中。這個報告并不能告訴你造成泄漏的是代碼還是某個對象,而是告訴你在內存模擬器中應該留意那些類型的請求。把程序服務器放到產品環境中并不會使他們變敏感,而是跟蹤性能的工具可以使你的工作變的更簡單化。

            虛假內存泄漏

            少數的一些問題看起來是內存泄漏實際上并非如此。

            我將這些情況稱為假泄漏,表現在下面幾種情況:

            1.?分析過早?
            2.?Session泄漏?
            3.?異常的持久區域
            這章節對這些假泄漏都進行了調查,描述了如何去判斷這些情況以及如何處理.

            不要過早分析

            為了在尋找內存泄漏的時候盡量減少出現判斷錯誤的可能性,你應當在適當的時候分析堆。危險是:一些生命周期長的對象需要裝載到堆中,因此在堆達到穩定狀態且包含了核心對象之前具有很大的欺騙性。在分析堆之前,應該讓應用程序達到穩定狀態。

            為了判斷是否過早的對堆進行分析,持續2個小時對跟蹤到的分析快照進行分析,看堆的使用率是上升還是下降。如果是下降,保存這個時候的內存記錄。如果是上升,這個時候就需要分析內存中的SESSION了。
          發生泄漏的session

            WEB請求經常導致內存泄漏,在一個WEB請求中,對象會被限制存儲在有限的幾個區域。這些區域就是:

            1.?頁面區域?
            2.?請求區域?
            3.?上下文區域?
            4.?應用程序區域?
            5.?靜態變量?
            6.?長生命周期的變量,例如SERVLET
            當實現一些JSP(JAVASERVER頁面)時,在頁面上聲明的變量在頁面結束的時候就被釋放,這些變量僅僅在這個單獨的頁面存在時存在。WEB服務器會向應用程序服務器傳送一系列參數和屬性,也就是在SERVLET和JSP之間傳輸HttpServletRequest中的對象。你的動態頁面依靠HttpServletRequest在不同的組件之間傳輸信息,但當請求完成或者socket結束的時候,SERVLET控制器會釋放所有在HttpServletRequest?中的對象。這些對象僅在他們的請求的生命周期內存在。

            HTTP是無狀態的,這意味著客戶向服務器發送一個請求,服務器回應這個請求,這個傳遞就完成了,就是會話結束了。我們應該感激WEB頁面幫我們做的日志,這樣我們就能向購物車放置東西,并去檢查它,服務器能夠定義一個跨越多請求的擴展對話。屬性和參數被放在各自用戶的HttpSession對象中,并通過它讓程序的SERVLET和JSP交流。利用這種辦法,頁面存儲你的信息并把他們添加到HttpSession中,因此你可以用購物車購買東西,并檢查商品和使用信用卡付帳。作為一個無狀態的協議,它總是客戶端發起連接請求,服務器需要知道一個會話存在多長時間,到時候就應該釋放這個用戶的數據。超過這個會話的最長時間就是會話超時,他們在程序服務器中設置。除非明確的要求釋放對象或者這個會話失效,否則在會話超時之前會話中的對象會一直存在。

            正如session是為每個用戶管理對象一樣,ServletContext為整個程序管理對象。ServletContext的有效范圍是整個程序,因此你可以利用Servlet中的ServletContext或者JSP應用程序對象在所有的Servlet和JSP之間讓在這個程序中的所有用戶共享數據。ServletContext是最主要的存放程序配置信息和緩存程序數據的地方,例如JNDI的信息。

            如果數據不是存儲這個四個地方(頁面范圍,請求范圍,會話范圍,程序范圍)那就可能存儲在下面的對象中:

            1.?靜態變量?
            2.?長生命周期的類變量
            每個類的靜態變量被JVM(JAVA虛擬機)所控制,他們存在與否和類是否已經被初始化無關。一個類的所有實例共用一個存儲靜態變量的地方,因此在任何一個實例中修改靜態變量會影響這個類的其他實例。因此,如果一個程序在靜態變量中存放了一個對象,如果這個變量生命周期沒有到,那么這個對象就不會被JVM釋放。這些靜態對象是造成內存泄漏的主要原因。

            最后,對象能夠被放到內部數據類型或者長生命周期類中的成員變量中,例如SERVLET。當一個SERVLET被創建并且被裝載到內存,它在內存中僅有一個實例,采用多線程去訪問這個SERVLET實例。如果在INIT()方法中裝載配置信息,將他存儲于類變量中,那么當需要維護的時候就可以隨時讀出這些信息,這樣所有的對象就用相同的配置。我常碰到的一個問題就是利用SERVLET類變量去存儲象頁面緩存這樣的信息。在他們自己內部本身存貯這些緩存配置是個不錯的選擇,但存貯在SERVLET中是最糟糕的情況。如果你需要使用緩存,你最好使用第三方控制插件,例如?TANGOSOL的COHERENCE。

            當在頁面或者請求范圍中利用變量存放對象的時候,在他們結束的時候這些對象會自動釋放。同樣,在SESSION中存放對象的時候,當程序明確說明此SESSION失效的或者會話執行超時的時候,這些對象才會自動被釋放。

            很多看起來象內存泄漏的情況都是上面的那些會話中的泄漏。一個造成泄漏的會話并不是泄漏了內存而是類似于泄漏,它消耗了內存,但最終這些內存都會被釋放的。如果程序服務器發生內存溢出,判斷是內存泄漏還是內存缺乏的最好的方法就是:停止所有向這個服務器所發的請求的對象,等待會話超時,看內存時候會被釋放出來。這雖然不會一定能夠達到你要的目的,但是這是最好的分段處理方法,當你裝載測試器的時候,你應該先掛斷你內容巨大的會話而不是先去尋找內存泄漏。

            通常來說,如果你執行了一個很大的會話,你應該盡量去減少它所占用的內存空間,如果可以的話最好能重構程序,以減少session所占據的內存空間。下面2種方法可以降低大會話和內存的沖突:

            1.?增大堆的空間以支持你的大會話?
            2.?縮短會話的超時時間,讓它能夠快速的失效
            一個巨大的堆會導致垃圾回收花費更多的時間,因此這不是一個好解決方法,但總比發生OutofMemoryError強。增加足夠的堆空間以使它能夠存儲所有應該保存的有效值,也意味著你必須有足夠的內存去存儲所有訪問你站點的用戶的有效會話。如果商業規則允許的話最好能縮短會話超時的時間,以減少堆占用空間的沖突。

          總結下,你應該依據合理性和重要性按下面的步驟依次去執行:

            1.?重構程序,盡量減少擁有session范圍的變量所存儲的信息量?
            2.?鼓勵你的客戶在他們使用完后,明確的釋放會話?
            3.?縮短超時的時間,以便于讓你內存盡快的得到回收?
            4.?增加你堆空間的大小
            無論如何,不要讓程序范圍級的變量,靜態變量,長生命周期的類存儲對象,事實上,你需要在內存模擬器中去分析泄漏。

            異常的持久空間

            容易誤解JVM為持久空間分配內存的目的。堆僅僅存儲類的實例,但JVM在堆中創建類實例之前,它必須把字節流文件(.class文件)裝載到程序內存中。它利用內存中的字節流在堆中創建類的實例。JVM利用程序的內存來裝載字節流文件,這個內存空間稱為持久空間。圖6顯示了持久空間和堆的關系:它存在于JVM程序中,并不是堆的一部分。

          Figure?6.?The?relationship?between?the?permanent?space?and?the?heap

            通常,你可能想讓你的持久空間足夠大以便于它能夠裝載你程序所有的類,因為很明顯,從文件系統中讀取類文件比從內存中裝載代價高很多。JVM提供了一個參數讓你不的程序不卸載已經裝載到持久空間中的類文件:

            ?noclassgc

            這個參數選項告訴JVM不要跑到持久空間去執行垃圾收集釋放其中已經裝載的類文件。這個參數選項很聰明,但是會引起一個問題:當持久空間滿了以后依然需要裝載新文件的時候JVM會怎么處理呢?我觀測到的資料說明:如果JVM檢測到持久空間還需要內存,就會調用主垃圾收集程序。垃圾收集器清除堆,但它并不會對持久空間進行任何操作,因此它的努力是白費的。于是JVM就再重新檢測持久空間,看它是否滿,然后再次執行程序,一遍的一遍重復。
           我第一次碰到這種問題的時候,用戶抱怨說程序性能很差勁,并且在運行了幾次后就出現了問題,可能是內存溢出問題。在我調查了詳細的關于堆和程序內存利用的收集器的記錄后,我迅速發覺堆的狀態非常正常,但程序確發生了內存溢出。這個用戶維持了數千的JSP頁面,在裝載到內存前把他們都編譯成了字節流文件放入持久空間。他的環境已經造成了持久空間溢出,但是在堆中由于用了?-noclassgc?選項,于是JVM并不去釋放類文件來裝載新的類文件。于是就導致了內存溢出錯誤,我把他的持久空間改為512M大小,并去掉了?-noclassgc?參數。

            正像圖7顯示的,當持久空間變滿了的時候,就引發垃圾收集,清理了樂園和幸存者空間,但是并不釋放持久空間中的一點內存。

          Figure?7.?Garbage?collection?behavior?when?the?permanent?space?becomes?full.?Click?on?thumbnail?to?view?full-sized?image.

            注意

            當設置持久空間大小時候,一般考慮128M,除非你的程序有很多的類文件,這個時候,你就可以考慮使用256M大小。如果你想讓他能夠裝載所有的類的時候,就會導致一個典型的結構錯誤。設置成512M就足夠了,它僅僅是暫時的時間的花費。把持久空間設置成512M大小就象給一個腳痛的人吃止痛藥,雖然暫時緩解了痛,但是腳還是沒有好,依然需要醫生把痛治療好,否則只是把問題延遲了而已。

            線程池

            外界同WEB或程序服務器連接的主要方法就是向他們發送請求,這些請求被放置到程序的執行次序隊列中。和內存最大的沖突就是程序服務器所設置的線程池的大小。線程池的大小就是程序可以同時處理的請求的數量。如果池太小,請求就需要在隊列中等待程序處理,如果太大,CPU就需要花費太多的時間在這些眾多的線程之間來回的切換。

            每個服務器都有一個SOCKET負責監聽。程序把接受到的請求放到待執行隊列中,然后將這個請求從隊列移動到線程中被程序處理。

            圖8顯示了服務器的處理程序。

          Figure?8.?服務器處理請求的次序結構
          線程池太小

            每當我碰到有人抱怨裝載速度的性能隨著裝載的數量的增加變的越來越糟糕的時候,我會首先檢查線程池。特別是,我在看到下面這些信息的時候:

            1.線程池的使用?
            2.很多請求等待處理(在隊列中等待處理)
            當一個線程池被待處理的請求裝滿的時候,響應的時間就變的極其糟糕,因為這些在隊列中等待處理的請求會消耗很多的額外時間。這個時候,CPU的利用率會非常低,因為程序服務器沒有時間去指揮CPU工作。這個時候,我會按一定幅度增加調節池的大小,并在未處理請求的數量減少前一直監視程序的吞吐量,你需要一個合理甚至更好的負載量者,一個精確的負載量測試工具可以準確的幫你測試出結果。當你觀測吞吐量的時候,如果你發現吞吐量降低了,你就應該把池的大小下調一個幅度,一直到找到讓它保持最大吞吐量的大小為止。

            圖9顯示了連接池太小的情況

          Figure?9.?所有的線程都被占用了,請求就只能在隊列中等待

            每當我閱讀性能調整手冊的時候,最讓我頭疼的就是他們從來不告訴你特殊情況下線程池應該是多大。由于這些值非常依賴程序的行為,他們只告訴你大普通情況下正確的大小,但是他們給了你一個范圍內的值,這對用戶很有利的。例如考慮下面2種情況::

            1.?一個程序從內存中讀出一個字符串,把它傳給JSP頁面,讓JSP頁面去顯示?
            2.?另一個程序從數據庫中讀出1000個數值,為這些不規則的數值求平均。第一個程序對請求的回應會很塊,大概僅需要不足0.25秒的時間,且不怎么占據CPU。第二個程序可能需要3秒去回應,同時會占據CPU。因此,為第一個程序配置的池大小是100就有點太小了,因為程序能夠同時處理200個;但為第二個程序配置的池是100,就有點太大了,因為CPU可能就能應付50個線程。
            但是,很多程序并沒有在這種情況下動態的去調整的功能。多數情況下是做相同的事,但是應該為他們劃分范圍。因此,我建議你為一個CPU分配50到75個左右的線程。對一些程序來說,這個數量可能太少,對另一個些來說可能太多,我剛開始為每個CPU分配50到75個線程,然后根據吞吐量和CPU的性能,并做適當的調整。

            線程池太大

            除了線程池數量太小之外的情況外,環境也可能把線程數量配置的過大。當這些環境中的負載量不斷增大的時候,CPU的使用率會持續無法降低,就沒有什么響應請求的時間了,因為CPU只顧的在眾多的線程之間來回的切換跳動,沒時間讓線程去做他們應該做的事了。

            連接池過大的最主要的跡象就是CPU的使用率一直很高。有些時候,垃圾收集也可能導致CPU使用率很高,但是垃圾收集導致的CPU使用率很高和池過大導致的使用率有一個主要的區別就是:垃圾收集引起的只是短時間的高使用率就象個釘子,而池過大導致的就是一直持續很高呈線性。

            這個情況發生的時候,請求會被放在隊列中不被處理,但是不會始終如此,因為請求占用CPU的情況和程序占用的情況造成的后果不同。降低線程池的大小可能會讓請求等待,但是讓請求等待總比為了處理請求而讓CPU忙不過來的好。讓CPU保持持續的高使用率,同時性能不降低,新請求到來的時候放入到隊列中,這是最理想的程序。考慮下面這個很類似的情況:很多高速公里有交通燈來保證車輛進入到擁擠的公里中。在我看來,這些交通燈根本沒用,道理很充分。比如你來了,在交通燈后面的安全線上等待進入到高速公路上。如果所有的車輛都同時涌向公里,我們就動彈不得,但是只要減緩涌向高速公路車輛的速度,交通遲早會暢通。事實上,很多的大城市都有這樣功能,但根本沒用,他們真正需要的是一些更多的小路(CPU),涌向高速公路的速度真的降低了,那么交通會變的正常起來。

          設置一個飽和的池,然后逐步減少連接池大小,一直到CPU占用率為75%到85%之間,同時用戶負載正常。如果等待隊列大小實在無法控制,考慮下面2中建議:

            1.把你的程序放入代碼模擬器運行,調整程序代碼?
            2.增加額外的硬件
            如果你的用戶負載超過了環境能承受的范圍,你應該考慮修正代碼減少和CPU的沖突或者增加CPU。

            JDBC連接池

            很多JAVA?EE?程序連接到一個后臺數據源,大多數是通過JDBC(JAVA?DATABASE?CONNECTIVITY)將程序和后臺連接起來。由于創建數據庫連接的代價很高,程序服務器讓在同一個程序服務器實例下的所有程序共享特定數量的一些連接。如果一個請求需要連接到數據庫,但是數據庫的連接池無法為這個請求創建一個新連接,這個時候請求就會停下來等待連接池完成自己的操作再給她分配一個連接。反過來,如果數據庫連接池太大程序服務器就會浪費資源,并且程序有可能強迫數據庫承受過量的負荷。我們調試的目的就是盡量減少請求的等待時間和飽和的資源之間之間的沖突,讓一個請求在數據庫外等待要比強迫數據庫好的多。

            一個程序服務器如果設置連接的數量不合理就會有下面這些特征:

            1.程序運行速度緩慢?
            2.CPU使用率低?
            3.數據庫連接池使用率非常高?
            4.線程等待數據庫的連接?
            5.線程使用率很高?
            6.請求隊列中有待處理的請求(潛在的)?
            7.數據庫CPU使用率很低(因為沒有足夠的請求能夠讓他繁忙起來)
            JDBC?prepared?statements

            和JDBC相關的另一個重要的設置就是:為JDBC使用的statement?所預設的緩存的大小。當你的程序在數據庫中運行SQL?statement?的時候三下面3個步驟進行:

            1.準備?
            2.執行?
            3.返回數值
            在準備階段,數據庫驅動器讓數據庫完成隊列中的執行計劃。執行的時候,數據庫執行語句并返回指向結果的引用。在返回的時候,程序重新描述這些結果并描述出這些被請求的信息。

            數據庫驅動會這樣優化程序:首先,你需要去準備一個statement?,這個statement?它會讓數據庫做好執行和緩存結果的準備。在此同時,數據庫驅動會從緩存中裝載已經準備好的statement?,而不用直接連接到數據庫。

            如果prepared?statement?設置太小,數據庫驅動器會被迫去查詢沒有裝載進緩存區的statement?,這就會增加額外的連接到數據庫的時間。prepared?statement?緩存區設置不恰當最主要的癥狀就是花費大量的時間去連接相同的statement。這段被浪費的時間本來是為了讓它去裝載后面的調用的。

            事情變的稍微復雜了點,緩存prepared?statement?是每個statement的基礎,就是說在一個statement連接之前都應當緩存起來。這個增加的復雜性就產生了一個沖突:如果你有100個prepared?statement需要去緩存,但你的連接池中有50個數據庫連接,這個時候你就需要有存放5000條預備語句的內存。

            通過跟蹤性能,確定出你程序所執行的不重復的statement?的數量,并從這些statement?中找出哪些條是頻繁執行的。

            Entity?bean(實體BEAN)和stateful?session?bean的緩沖

            無狀態(stateless)對象可以被放入到池中共享,但象Entity?beans和?stateful?session?bean這樣的有狀態的對象就需要被緩存,因為這些bean的每個實例都是不相同的。當你需要一個有狀態對象時,你需要明確創建這個對象的特定實例,普通的實例是不能滿足的。類似的,你考慮一個超市類似的情況,你需要個售貨員但他叫什么并不重要,任何售貨員都可以滿足你。也就是,售貨員被放入池中共享,因為你只需要是售貨員就可以,而不是一個叫做史締夫的這個售貨員。當你離開超市的時候,你需要帶上你的孩子,不是其他人的孩子,而是你自己的。這個時候,孩子就需要被緩存。

          Figure?10.?The?application?requests?an?object?from?the?cache?that?is?in?the?cache,?so?a?reference?to?that?object?is?returned?without?making?a?network?trip?to?the?database
          當你的緩存區太小的時候,緩存的性能就會明顯的受到影響。特別是,當一個請求去一個已經滿了的緩存區域去請求一個對象的時候,下面的步驟就會執行,這些步驟會在圖11中顯示:

            1.?程序請求一個對象?
            2.?緩存檢測這個對象是否已經存在于緩存中?
            3.?緩存決定把一個對象開除出緩存(一般采用的算法是遺棄最近使用次數最少的對象)?
            4.?把這個對象扔出緩存(稱為passivated)?
            5.?把從數據庫中裝載這個新對象并放入到緩存(稱為activated)?
            6.?把指向這個對象的引用返回給程序

           Figure?11.?Because?the?requested?object?is?not?in?the?cache,?an?object?must?be?selected?for?removal?from?the?cache?and?removed?from?it.

            如果多數的請求都需要執行這些步驟的話,那你采用緩存技術就不是好的選擇了!如果這些處理步驟頻繁發生的話,你就需要重新推敲下你的緩存了。回憶一下:從緩存中去除一個對象稱為passivation,從持久存儲區取出一個對象放入緩存稱為activation。能在緩存中找到的請求(緩存中有此請求的對象)的百分率稱為hit?ratio,相反找不到的請求的百分率稱為miss?ratio。

            緩存剛被初始化的時候,hit?ratio是0,它的activation數量非常高,因此在初始化后你需要去觀察緩存的性能。初始化以后,你應該跟蹤passivation的數量并把它和與向緩存請求對象的請求的總量相比較,因為passivations只會發生在緩存被初始化以后。但一般來說,我們更需要關心緩存的miss?ratio。如果miss?ratio超過25%,那么緩存可能是太小了。因此,如果missratio的數量超過75%,那么不是你的緩存設置的太小就是你不需要緩存這個技術。

            一旦你覺得你的緩存太小,就去嘗試著增大大小,并測試增加的性能。如果miss?ration下降到20%以下,那你的緩存的大小就非常棒了,如果沒有什么效果,那么你就需要和這個程序的技術員聯系,看是這個對象是不是需要緩存或者是否應該修正程序中這個對象的代碼。

            Staless?session?bean和message-driven?bean池

            Stateless?session?bean?和message-driven?bean?在商業應用方面很重要,不要期望它們會保持自己特有的狀態信息。當你的程序需要使用這些BEAN的商業功能的時候,它就從一個池中取出一個BEAN實例,用這個實例來調用一個個方法,用完后再將BEAN的實例再放回到池中。如果你的程序過了一會又需要這個一摸一樣的BEAN,就從池中再得到一個實例,但不能保證你得到的就是上一個實例。池能夠讓程序共享資源,但是會讓你的程序付出潛在的等待時間。如果你無法從池中得到想要的BEAN,請求就會等待,一直到這個BEAN被放入到池中。很多程序服務器都會把這些池調整的很好,但是我碰到過因為在環境中把他們設置的太小而引發的不少麻煩。Stateless?bean池的大小應該和可執行線程池的大小一般大,因為一個線程同時只能使用一個對象,再多了就造成浪費的。因此,一些程序服務器把池的大小和線程的數量設置成同樣的數量。為了保險起見,你應該親自把它設置成這個數。
          事務

            使用Enterprise?Java的一個好處就是它天生就支持事務。通過JAVAEE?5?EJB(Enterprise?javaBeans)的注釋,你可以控制事務中方法的使用。事務會以下面2中方式結束:

            1.?事務提交?
            2.?事務回滾
            當一個事務被提交的時候,說明它已經完全成功了,但是當它回滾的時候,就說明發生了一些錯誤。回滾會是下面2種情況:

            1.?程序造成的回滾(程序回滾)?
            2.?非程序造成的回滾(非程序回滾)
            通常,程序回滾是因為商業的規定。比如一個WEB程序做一個素描畫的價格的調查,程序可能讓用戶輸入年齡,并且商業規定18歲以上才可以進入。如果一個16歲的提交了信息,那么程序就會拋出一個錯誤,打開一個網頁告訴他,他年齡還不能參與到這個信息的調查。因為程序拋出了異常,因此包含在程序中的事務的就會發生回滾。這只是普通的程序回滾,只有當發生大量的程序回滾才值得我們注意。

            另一方面,非程序回滾是非常糟糕的。有三種情形的非程序回滾:

            1.?系統回滾?
            2.?超時回滾?
            3.?資源回滾
            系統回滾意味著程序服務器中的一些東西非常的糟糕,恢復的幾率很渺茫。超時回滾就是當程序服務器中的程序處理請求時超時;除非你把超時設置的很短才會出現這種錯誤。資源回滾就是當一個程序服務器管理內部的資源的時候發生錯誤。例如,如果你設置你的程序服務器通過一個簡單的SQL語句去測試數據庫的連接,但數據庫對于程序服務器來說是無法連接的,這個時候任何和這個資源相關的事情都會發生資源回滾。

            如果發生非程序回滾,我們應該立刻注意,這個是不小的問題,但是你也需要留意程序回滾發生的頻率。很多時候人們對發生的異常很敏感,因此你需要哪些異常對你程序來說才是重要的。

            總結

            盡管各個程序和他們的環境都各不相同,但是有一些共同的問題困擾著他們。這篇文章的注意力并不是放在程序代碼的問題上,因為把注意力放在因為環境的問題而導致的低性能的問題上:

            1.內存溢出?
            2.線程池大小?
            3.JDBC連接池大小?
            4.JDBC預先聲明語句緩存大小?
            5.緩存大小?
            6.池大小?
            7.執行事務時候的回滾
            為了有效的診斷性能的問題,你應該了解什么問題會導致什么樣的癥狀。如果主要是程序的代碼導致的惡果那你應該帶著問題去尋求負責代碼的人尋求幫助,但是如果問題是由環境引起的,那么就要依靠你的操作來解決了。

            問題的根源依賴于很多要素,但是一些指示器可以增加一些你處理問題時候的一些信心,依靠他們可以完全排除一些其他的原因。我希望這個文章能對你排解JAVAEE環境問題起到幫助。
          posted on 2007-03-31 21:13 MEYE 閱讀(2057) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 黔西县| 阳城县| 房山区| 东方市| 汶上县| 吴桥县| 淅川县| 康保县| 抚顺县| 竹山县| 湛江市| 安陆市| 随州市| 祁门县| 于都县| 新建县| 威远县| 寿光市| 胶州市| 逊克县| 广宗县| 门源| 双流县| 绥滨县| 蒙自县| 通山县| 黄骅市| 云阳县| 馆陶县| 新河县| 桐城市| 甘德县| 顺平县| 大悟县| 巧家县| 延边| 顺义区| 文化| 金华市| 高要市| 曲松县|