<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0
    一篇關(guān)于JSF的入門文章
    轉(zhuǎn)貼?

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

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

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

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

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

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

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

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

      3.?JSF優(yōu)勢之二:事件驅(qū)動模式

      事件是面向?qū)ο蠓椒ǖ闹匾M成部分,對象之間通過事件進(jìn)行溝通和交流,使得一個或多個對象能夠?qū)α硪粋€對象的行為作出響應(yīng),共同合作去完成一項(xiàng)業(yè)務(wù)邏輯。通常,編寫Web程序時(shí),程序員要為對象之間的溝通設(shè)計(jì)機(jī)制,編寫代碼。雖然溝通的內(nèi)容屬于業(yè)務(wù)邏輯,但溝通的機(jī)制顯然與業(yè)務(wù)沒有太大關(guān)系,程序員因此為業(yè)務(wù)邏輯之外的功能浪費(fèi)了時(shí)間。JSF改變了這種狀況。JSF的事件和偵聽模式與大家熟悉的Javabean的事件模式類似,有Java基礎(chǔ)的程序員并不需要學(xué)習(xí)任何新的東西。JSF的UI組件可以產(chǎn)生事件,例如,當(dāng)頁面上一個文本輸入框的內(nèi)容被修改時(shí),會發(fā)出一個“值改變事件”。另一個對象如果對“值改變事件”感興趣,只需注冊為該對象的偵聽者,并編寫處理例程,即可命令JSF在事件發(fā)生時(shí)自動調(diào)用處理例程。JSF做了所有該做的事,留給程序員的只有業(yè)務(wù)邏輯代碼的編寫。?

      4.?JSF優(yōu)勢之三:用戶界面到業(yè)務(wù)邏輯的直接映射

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

      5.?JSF優(yōu)勢之四:程序員和網(wǎng)頁設(shè)計(jì)人員的分工

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

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

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

      ?7.?JSF優(yōu)勢之六:伴隨工具而生存

      JSF帶來了Web編程的巨大變革,變革的強(qiáng)烈程度超出了很多工具廠商的預(yù)料,以至于現(xiàn)在可供JSF使用的工具非常缺乏。缺乏工具支持的JSF只會令人敬而遠(yuǎn)之,因此,JSF在設(shè)計(jì)之初就為工具廠商預(yù)留了用武之地。在為數(shù)不多的JSF工具中,sun的Java?Studio?Creator是一個優(yōu)秀的開發(fā)環(huán)境;Borland的Jbuilder在JSF1.1時(shí)曾經(jīng)是非常好用的開發(fā)工具,可惜現(xiàn)在對JSF1.2的支持沒有跟上;Eclipse下JSF的插件很多,但真正支持所見即所得的JSF插件都是收費(fèi)的,例如Bea的Workshop?for?JSF、Exadel的JSF?Studio等等;此外,Oracle和IBM也有JSF的開發(fā)工具。隨著工具的不斷涌現(xiàn),用JSF開發(fā)Web程序?qū)絹碓椒奖愫涂焖佟?

      ?8.?JSF優(yōu)勢之七:全面的用戶自定義支持

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

      9.?JSF優(yōu)勢之八:Web開發(fā)的官方標(biāo)準(zhǔn)之一

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

      10.?結(jié)束語:該你了,JSF!

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

      英文網(wǎng)頁數(shù)量(萬)?日文網(wǎng)頁數(shù)量(萬)?繁體網(wǎng)頁數(shù)量(萬)?簡體網(wǎng)頁數(shù)量(萬)

      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

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

      11.?后記:不同的聲音

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

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




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

    摘要:
    EJB是一種企業(yè)應(yīng)用技術(shù),旨在建立一個企業(yè)應(yīng)用開發(fā)框架,但從其誕生之日起,質(zhì)疑之聲一直不斷。EJB是企業(yè)應(yīng)用框架的先驅(qū),在企業(yè)應(yīng)用框架的方法論上有獨(dú)到的見解,雖然存在不少缺陷,但仍不失為企業(yè)應(yīng)用框架的理想。

    1.?備受爭議的EJB

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

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

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

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

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

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

      2.?EJB是企業(yè)應(yīng)用的先驅(qū)

      筆者接觸第一個企業(yè)應(yīng)用,是在1997年。那時(shí)PowerBuilder風(fēng)頭正勁,不過,多數(shù)人使用PowerBuilder,是因?yàn)樗臄?shù)據(jù)窗口。當(dāng)時(shí)筆者在一個項(xiàng)目中遇到一個難題,那就是如何把一臺服務(wù)器上的應(yīng)用一分為二,跑在兩臺服務(wù)器上,以提高性能。這是典型的分布式應(yīng)用,雖然不是一個完整意義上的企業(yè)應(yīng)用,不過,因?yàn)閼?yīng)用中需要用到分布式的概念,多少也算和企業(yè)應(yīng)用沾上邊了。

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

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

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

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

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

      3.?EJB是一種思想

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

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

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

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

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

    1.?架構(gòu)概述?


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


    各種因數(shù)初步確定?

    為了形成架構(gòu)基線,架構(gòu)設(shè)計(jì)師要對平臺(體系)中的技術(shù)進(jìn)行篩選,各種利弊的權(quán)衡。往往架構(gòu)設(shè)計(jì)師在這個過程中要閱讀大量的技術(shù)資料,聽取項(xiàng)目組成員的建議,考慮領(lǐng)域?qū)<业男枨螅紤]贊助商成本(包括開發(fā)成本和運(yùn)行維護(hù)成本)限額。一旦架構(gòu)設(shè)計(jì)經(jīng)過評審,這些因數(shù)初步地就有了在整個項(xiàng)目過程中的對項(xiàng)目起多大作用的定位。?

    定向技術(shù)培訓(xùn)?

    一旦架構(gòu)師設(shè)計(jì)的架構(gòu)得到了批準(zhǔn)形成了基線,項(xiàng)目開發(fā)和運(yùn)行所采用的技術(shù)基本確定下來了。眾多的項(xiàng)目經(jīng)理都會對預(yù)備項(xiàng)目組成員的技術(shù)功底感到擔(dān)心;他們需要培訓(xùn)部門提供培訓(xùn),但就架構(gòu)師面對的技術(shù)海洋,項(xiàng)目經(jīng)理根本就提不出明確的技術(shù)培訓(xùn)需求。怎不能夠?qū)w系中所有技術(shù)都進(jìn)行培訓(xùn)吧!有了架構(gòu)里程碑基線,項(xiàng)目經(jīng)理能確定這個項(xiàng)目開發(fā)會采用什么技術(shù),這是提出培訓(xùn)需求應(yīng)該是最精確的。不過在實(shí)際項(xiàng)目開發(fā)中,技術(shù)培訓(xùn)可以在基線確定之前與架構(gòu)設(shè)計(jì)并發(fā)進(jìn)行。?

    角色分工?

    有了一個好的架構(gòu)藍(lán)圖,我們就能準(zhǔn)確劃分工作。如網(wǎng)頁設(shè)計(jì),JSP?標(biāo)簽處理類設(shè)計(jì),SERVLET?設(shè)計(jì),session?bean設(shè)計(jì),還有各種實(shí)現(xiàn)。這些任務(wù)在架構(gòu)藍(lán)圖上都可以清晰地標(biāo)出位置,使得項(xiàng)目組成員能很好地定位自己的任務(wù)。一個好的架構(gòu)藍(lán)圖同時(shí)也能規(guī)范化任務(wù),能很好地把任務(wù)劃分為幾類,在同一類中的任務(wù)的工作量和性質(zhì)相同或相似。這樣工作量估計(jì)起來有一個非常好的基礎(chǔ)。?

    運(yùn)行維護(hù)?

    前面說過各個任務(wù)在架構(gòu)圖上都有比較好的定位。任何人能借助它很快地熟悉整個項(xiàng)目的運(yùn)行情況,錯誤出現(xiàn)時(shí)能比較快速地定位錯誤點(diǎn)。另外,有了清晰的架構(gòu)圖,項(xiàng)目版本管理也有很好的版本樹軀干。?

    擴(kuò)展性?

    架構(gòu)猶如一顆參天大樹的軀干,只要軀干根系牢,樹干粗,長一些旁支,加一些樹葉輕而易舉無疑。同樣,有一個穩(wěn)定的經(jīng)得起考驗(yàn)的架構(gòu),增加一兩個業(yè)務(wù)組件是非常快速和容易的。?

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


    1.1.?模型1?


    模型1其實(shí)不是一個什么穩(wěn)定架構(gòu),甚至談不上形成了架構(gòu)。模型1的基礎(chǔ)是JSP文件。它從HTTP的請求中提取參數(shù),調(diào)用相應(yīng)的業(yè)務(wù)邏輯,處理HTTP會話,最后生成HTTP文檔。一系列這樣的JSP文件形成一個完整的模型1應(yīng)用,當(dāng)然可能會有其他輔助類或文件。早期的ASP?和?PHP?技術(shù)就屬于這個情況。?


    總的看來,這個模型的好處是簡單,但是它把業(yè)務(wù)邏輯和表現(xiàn)混在一塊,對大應(yīng)用來說,這個缺點(diǎn)是令人容忍不了的。?


    1.2.?模型2?


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


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


    1.3.?多層應(yīng)用?


    下圖為J2EE體系中典型的多層應(yīng)用模型。?


    Client?tier客戶層?

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

    WEB?tier?WEB應(yīng)用層?

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

    EJB?tier?企業(yè)組件層?

    企業(yè)組件層由EJB容器運(yùn)行,支持EJB,?JMS,?JTA?等服務(wù)和技術(shù)。?

    EIS?tier?企業(yè)信息系統(tǒng)層?

    企業(yè)信息系統(tǒng)包含企業(yè)內(nèi)傳統(tǒng)信息系統(tǒng)如財(cái)務(wù),CRM等,特點(diǎn)是有數(shù)據(jù)庫系統(tǒng)的支持。?


    應(yīng)用框架目前主要集中在WEB層,旨在規(guī)范這一層軟件的開發(fā)。其實(shí)企業(yè)組件層也可以實(shí)現(xiàn)這個模型,但目前主要以設(shè)計(jì)模式的形式存在。而且有些框架可以擴(kuò)充,有了企業(yè)組件層組件的參與,框架會顯得更緊湊,更自然,效率會更高。?


    2.?候選方案?


    目前,實(shí)現(xiàn)模型2的框架也在不斷的涌現(xiàn),下面列出比較有名的框架。?


    2.1.?Apache?Struts?


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


    2.1.1.?Struts和MVC?


    模型2的目的和MVC的目的是一樣的,所以模型2基本可以和MVC等同起來。下圖體現(xiàn)了Struts的運(yùn)作機(jī)理:?


    2.1.1.1.?控制?


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


    2.1.1.2.?模型?


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


    Form?beans(表單Beans)?

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

    業(yè)務(wù)邏輯beans?

    專門用來處理業(yè)務(wù)邏輯。?

    系統(tǒng)狀態(tài)beans?

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

    2.1.1.3.?視圖?


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


    2.1.2.?Struts的細(xì)節(jié)分析?


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


    用戶發(fā)出一個*.do的HTTP請求,控制組件接收到這個請求后,查找針對這個請求的動作映射,再檢查是否曾創(chuàng)建過相應(yīng)的動作對象(action實(shí)例),如果沒有則調(diào)用actionmapping生成一個動作對象,控制組件會保存這個動作對象供以后使用。接著調(diào)用actionmapping的方法得到actionForm對象。之后把a(bǔ)ctionForm作為參數(shù)傳給動作對象的perform方法,這個方法結(jié)束之后會返回給控制組件一個?actionforward對象。控制組件接著從這個對象中獲取下一個視圖的路徑和重定向?qū)傩浴H绻麨橹囟ㄏ騽t調(diào)用HTTPSERVLETREPONSE的方法來顯示下一個視圖,否則相繼調(diào)用requestdispatcher,?SERVLETcontext的方法續(xù)傳HTTP請求到下一個視圖。?


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


    2.1.2.2.?模型到視圖?


    模型到視圖指視圖在顯示之前裝載系統(tǒng)數(shù)據(jù)到視圖的過程。系統(tǒng)數(shù)據(jù)一般為模型內(nèi)java?bean的信息。示意圖表現(xiàn)了由控制組件forward過來的有html:form定制標(biāo)簽的JSP?的處理邏輯。?


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


    2.1.3.?優(yōu)缺點(diǎn)?


    優(yōu)點(diǎn):?


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

    作為開源項(xiàng)目,有很多先進(jìn)的實(shí)現(xiàn)思想?

    對大型的應(yīng)用支持的較好?

    有集中的網(wǎng)頁導(dǎo)航定義?

    缺點(diǎn):?


    不是業(yè)屆標(biāo)準(zhǔn)?

    對開發(fā)工具的支持不夠?

    復(fù)雜的taglib,需要比較長的時(shí)間來掌握?

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

    修改建議?

    把a(bǔ)ctionform屬性的設(shè)置器和訪問器修改成讀取或生成xml文檔的方法,然后?html?form和actionform之間用xml文檔進(jìn)行數(shù)據(jù)交換,使之松散耦合,適應(yīng)數(shù)據(jù)結(jié)構(gòu)易變化的應(yīng)用。?


    2.2.?JATO?


    JATO應(yīng)用程序框架是iPlanet?應(yīng)用程序框架的舊名。它是一個成熟的、強(qiáng)大的,基于J2EE標(biāo)準(zhǔn)的面向于開發(fā)WEB應(yīng)用程序的應(yīng)用框架。結(jié)合了顯示字段、應(yīng)用程序事件、組件層次和以頁面為中心的開發(fā)方法、以及MVC和服務(wù)到工作者service-to-workers的設(shè)計(jì)模式等概念。JATO可適用于中、大、超大規(guī)模的WEB應(yīng)用。但是它也不是一個企業(yè)層的應(yīng)用框架,也就是說它不會直接提供創(chuàng)建EJB,?WEB?services等企業(yè)層組件的方法,但用它可以構(gòu)造出訪問企業(yè)層組件的客戶應(yīng)用。?


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


    iPlanet應(yīng)用框架核心;?

    iPlanet應(yīng)用框架組件;?

    iPlanet應(yīng)用框架擴(kuò)展。?

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


    JATO最大的威力在:對于快速開發(fā)用戶,你能利用框架組件和擴(kuò)展提高生產(chǎn)率,對于要求更大靈活性的用戶,你能實(shí)現(xiàn)框架核心提供的接口來保持應(yīng)用的框架兼容性。?

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


    JATO標(biāo)簽庫提供了VIEW對象與JSP文件的接口。庫中標(biāo)簽處理程序負(fù)責(zé)實(shí)現(xiàn)VIEW對象和JSP產(chǎn)生地客戶端文檔的信息同步和交換。這個圖清楚地表達(dá)了這種對應(yīng)關(guān)系?


    2.2.1.?MVC分析?


    前端控制組件接收用戶發(fā)來的任何請求,這個可在WEB.xml中指定請求分發(fā)組件負(fù)責(zé)視圖管理和導(dǎo)航,和前端控制組件封裝在ApplicationSERVLETBase一起實(shí)現(xiàn)。應(yīng)用程序開發(fā)者需要為每一個子系統(tǒng)(人力資源,財(cái)務(wù),CRM等)實(shí)現(xiàn)一個此類的繼承。?


    請求分發(fā)組件分發(fā)請求給工作者,工作者實(shí)現(xiàn)了command接口。應(yīng)用開發(fā)者可以實(shí)現(xiàn)這個接口。JATO提供了一個缺省實(shí)現(xiàn):DefaultRequestHandingCommand,這個實(shí)現(xiàn)會把請求傳給視圖組件的特定事件。?


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


    2.2.2.?優(yōu)缺點(diǎn)分析?


    優(yōu)點(diǎn):?


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

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

    對大型的應(yīng)用支持較好?

    缺點(diǎn):?


    不是業(yè)屆標(biāo)準(zhǔn)?

    目前還沒有開發(fā)工具的支持(然JATO已經(jīng)為工具支持做好了準(zhǔn)備)?

    沒有定義網(wǎng)頁導(dǎo)航,開發(fā)者在視圖中自己指定具體的導(dǎo)航URL?

    修改建議?

    把眾多的VIEW/MODEL對應(yīng)修改成xml文檔傳遞數(shù)據(jù),加上集中的網(wǎng)頁導(dǎo)航定義?


    2.3.?JSF(JavaServer?Faces)?


    JSF是一個包括SUN在內(nèi)的專家組正在定義的開發(fā)WEB應(yīng)用用戶界面的框架,JSF?技術(shù)包括:?


    一組API,它實(shí)現(xiàn)UI了組件,管理組件的狀態(tài),處理事件,輸入校驗(yàn),定義頁面導(dǎo)航,支持國際化和訪問;?

    一個JSP定制標(biāo)簽庫實(shí)現(xiàn)與JSP的接口。?

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


    2.3.1.?STRUTS、JATO和JSF比較?


    它們之間有部分重疊,但重點(diǎn)不一樣。?


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

    JSF的規(guī)范的發(fā)布版將在?2002年底發(fā)布,實(shí)現(xiàn)可能要比這個時(shí)間晚些。另外將會有工具支持這個框架的應(yīng)用開發(fā)。?

    2.4.?WAF?


    WAF是WEB?APPLICATION?FRAMWORK的簡稱,是SUN藍(lán)皮書例子程序中提出的應(yīng)用框架。它實(shí)現(xiàn)了?MVC和其他良好的設(shè)計(jì)模式。?


    2.4.1.?細(xì)節(jié)分析?



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


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


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


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


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


    2.4.3.?優(yōu)缺點(diǎn)分析?


    優(yōu)點(diǎn)?


    屏幕導(dǎo)航定義明確?

    為框架的擴(kuò)展提供了一個空間?

    缺點(diǎn)?


    源碼比較亂,穩(wěn)定性和可靠性沒人驗(yàn)證。?

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

    沒有模型到視圖的定義?

    修改意見?

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

    這篇文章,是PRO?JAVA?EE?5?Performance?Management?and?Optimization?的一個章節(jié),作者Steven?Haines分享了他在調(diào)優(yōu)企業(yè)級JAVA應(yīng)用時(shí)所遇到的常見問題。

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

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

      內(nèi)存溢出錯誤

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

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

      ----應(yīng)用服務(wù)器崩潰?
      ----性能下降?
      ----一個看起來好像無法結(jié)束的死循環(huán)在重復(fù)不斷的執(zhí)行垃圾收集,它會導(dǎo)致程序停止運(yùn)行,并且經(jīng)常導(dǎo)致應(yīng)用服務(wù)器崩潰
      不管癥狀是什么,如果你想讓程序恢復(fù)正常運(yùn)行,你一般都需要重新啟動應(yīng)用服務(wù)器。

      引發(fā)out-of-memory?錯誤的原因

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

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

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

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

      SUN?JVM的內(nèi)存管理方式

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

      SUN?JVM會劃分為:

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

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

      圖2和圖3展示的是了小幅度收集如何運(yùn)行

    圖2。對象在EDEN被創(chuàng)建一直到這個空間變滿。

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

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

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

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

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

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

     圖5。陰影表示在經(jīng)過大幅度的收集后幸存下來的對象,這些對象是潛在可能引發(fā)內(nèi)存泄漏的對象

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

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

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

    IBM?JVM內(nèi)存管理模式

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

      如何分析內(nèi)存泄漏

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

      1.?在內(nèi)存模擬器中運(yùn)行你的應(yīng)用程序?
      2.?執(zhí)行使用方案(制造請求)以便讓程序在內(nèi)存中裝載請求所需要的所有的對象,這可以為以后詳細(xì)的分析排除不必要的干擾?
      3.?在執(zhí)行使用方案前對堆進(jìn)行拍照以便捕獲其中所有運(yùn)行的對象。?
      4.?再次運(yùn)行使用方案。?
      5.?再次拍照,來捕獲使用方案運(yùn)行之后堆中所有對象的狀態(tài)。?
      6.?比較這2個快照,找出執(zhí)行使用方案后本不應(yīng)該出現(xiàn)在堆中的對象。
      這個時(shí)候,你需要去和開發(fā)者交流,告訴他你所碰到的棘手的請求,他們可以判斷究竟是對象泄漏還是為了某個目的特地讓對象保留下來的。如果執(zhí)行完后并沒有發(fā)現(xiàn)內(nèi)存泄漏的情況,我一般會轉(zhuǎn)到步驟4再進(jìn)行多次類似的跟蹤。比如,我可能會將我的請求反復(fù)運(yùn)行17次,希望我的泄漏分析能夠得到17個情況(或更多)。這個方法不一定總有用,但如果是因?yàn)檎埱笠鸬膶ο笮孤┑脑挘蜁泻艽蟮膸椭?br />
      如果你無法明確的判斷泄漏是因?yàn)檎埱笠l(fā)的,你有2個選擇:

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

      虛假內(nèi)存泄漏

      少數(shù)的一些問題看起來是內(nèi)存泄漏實(shí)際上并非如此。

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

      1.?分析過早?
      2.?Session泄漏?
      3.?異常的持久區(qū)域
      這章節(jié)對這些假泄漏都進(jìn)行了調(diào)查,描述了如何去判斷這些情況以及如何處理.

      不要過早分析

      為了在尋找內(nèi)存泄漏的時(shí)候盡量減少出現(xiàn)判斷錯誤的可能性,你應(yīng)當(dāng)在適當(dāng)?shù)臅r(shí)候分析堆。危險(xiǎn)是:一些生命周期長的對象需要裝載到堆中,因此在堆達(dá)到穩(wěn)定狀態(tài)且包含了核心對象之前具有很大的欺騙性。在分析堆之前,應(yīng)該讓應(yīng)用程序達(dá)到穩(wěn)定狀態(tài)。

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

      WEB請求經(jīng)常導(dǎo)致內(nèi)存泄漏,在一個WEB請求中,對象會被限制存儲在有限的幾個區(qū)域。這些區(qū)域就是:

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

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

      正如session是為每個用戶管理對象一樣,ServletContext為整個程序管理對象。ServletContext的有效范圍是整個程序,因此你可以利用Servlet中的ServletContext或者JSP應(yīng)用程序?qū)ο笤谒械腟ervlet和JSP之間讓在這個程序中的所有用戶共享數(shù)據(jù)。ServletContext是最主要的存放程序配置信息和緩存程序數(shù)據(jù)的地方,例如JNDI的信息。

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

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

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

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

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

      通常來說,如果你執(zhí)行了一個很大的會話,你應(yīng)該盡量去減少它所占用的內(nèi)存空間,如果可以的話最好能重構(gòu)程序,以減少session所占據(jù)的內(nèi)存空間。下面2種方法可以降低大會話和內(nèi)存的沖突:

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

    總結(jié)下,你應(yīng)該依據(jù)合理性和重要性按下面的步驟依次去執(zhí)行:

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

      異常的持久空間

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

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

      通常,你可能想讓你的持久空間足夠大以便于它能夠裝載你程序所有的類,因?yàn)楹苊黠@,從文件系統(tǒng)中讀取類文件比從內(nèi)存中裝載代價(jià)高很多。JVM提供了一個參數(shù)讓你不的程序不卸載已經(jīng)裝載到持久空間中的類文件:

      ?noclassgc

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

      正像圖7顯示的,當(dāng)持久空間變滿了的時(shí)候,就引發(fā)垃圾收集,清理了樂園和幸存者空間,但是并不釋放持久空間中的一點(diǎn)內(nèi)存。

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

      注意

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

      線程池

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

      每個服務(wù)器都有一個SOCKET負(fù)責(zé)監(jiān)聽。程序把接受到的請求放到待執(zhí)行隊(duì)列中,然后將這個請求從隊(duì)列移動到線程中被程序處理。

      圖8顯示了服務(wù)器的處理程序。

    Figure?8.?服務(wù)器處理請求的次序結(jié)構(gòu)
    線程池太小

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

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

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

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

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

      1.?一個程序從內(nèi)存中讀出一個字符串,把它傳給JSP頁面,讓JSP頁面去顯示?
      2.?另一個程序從數(shù)據(jù)庫中讀出1000個數(shù)值,為這些不規(guī)則的數(shù)值求平均。第一個程序?qū)φ埱蟮幕貞?yīng)會很塊,大概僅需要不足0.25秒的時(shí)間,且不怎么占據(jù)CPU。第二個程序可能需要3秒去回應(yīng),同時(shí)會占據(jù)CPU。因此,為第一個程序配置的池大小是100就有點(diǎn)太小了,因?yàn)槌绦蚰軌蛲瑫r(shí)處理200個;但為第二個程序配置的池是100,就有點(diǎn)太大了,因?yàn)镃PU可能就能應(yīng)付50個線程。
      但是,很多程序并沒有在這種情況下動態(tài)的去調(diào)整的功能。多數(shù)情況下是做相同的事,但是應(yīng)該為他們劃分范圍。因此,我建議你為一個CPU分配50到75個左右的線程。對一些程序來說,這個數(shù)量可能太少,對另一個些來說可能太多,我剛開始為每個CPU分配50到75個線程,然后根據(jù)吞吐量和CPU的性能,并做適當(dāng)?shù)恼{(diào)整。

      線程池太大

      除了線程池?cái)?shù)量太小之外的情況外,環(huán)境也可能把線程數(shù)量配置的過大。當(dāng)這些環(huán)境中的負(fù)載量不斷增大的時(shí)候,CPU的使用率會持續(xù)無法降低,就沒有什么響應(yīng)請求的時(shí)間了,因?yàn)镃PU只顧的在眾多的線程之間來回的切換跳動,沒時(shí)間讓線程去做他們應(yīng)該做的事了。

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

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

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

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

      JDBC連接池

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

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

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

      和JDBC相關(guān)的另一個重要的設(shè)置就是:為JDBC使用的statement?所預(yù)設(shè)的緩存的大小。當(dāng)你的程序在數(shù)據(jù)庫中運(yùn)行SQL?statement?的時(shí)候三下面3個步驟進(jìn)行:

      1.準(zhǔn)備?
      2.執(zhí)行?
      3.返回?cái)?shù)值
      在準(zhǔn)備階段,數(shù)據(jù)庫驅(qū)動器讓數(shù)據(jù)庫完成隊(duì)列中的執(zhí)行計(jì)劃。執(zhí)行的時(shí)候,數(shù)據(jù)庫執(zhí)行語句并返回指向結(jié)果的引用。在返回的時(shí)候,程序重新描述這些結(jié)果并描述出這些被請求的信息。

      數(shù)據(jù)庫驅(qū)動會這樣優(yōu)化程序:首先,你需要去準(zhǔn)備一個statement?,這個statement?它會讓數(shù)據(jù)庫做好執(zhí)行和緩存結(jié)果的準(zhǔn)備。在此同時(shí),數(shù)據(jù)庫驅(qū)動會從緩存中裝載已經(jīng)準(zhǔn)備好的statement?,而不用直接連接到數(shù)據(jù)庫。

      如果prepared?statement?設(shè)置太小,數(shù)據(jù)庫驅(qū)動器會被迫去查詢沒有裝載進(jìn)緩存區(qū)的statement?,這就會增加額外的連接到數(shù)據(jù)庫的時(shí)間。prepared?statement?緩存區(qū)設(shè)置不恰當(dāng)最主要的癥狀就是花費(fèi)大量的時(shí)間去連接相同的statement。這段被浪費(fèi)的時(shí)間本來是為了讓它去裝載后面的調(diào)用的。

      事情變的稍微復(fù)雜了點(diǎn),緩存prepared?statement?是每個statement的基礎(chǔ),就是說在一個statement連接之前都應(yīng)當(dāng)緩存起來。這個增加的復(fù)雜性就產(chǎn)生了一個沖突:如果你有100個prepared?statement需要去緩存,但你的連接池中有50個數(shù)據(jù)庫連接,這個時(shí)候你就需要有存放5000條預(yù)備語句的內(nèi)存。

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

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

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

    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
    當(dāng)你的緩存區(qū)太小的時(shí)候,緩存的性能就會明顯的受到影響。特別是,當(dāng)一個請求去一個已經(jīng)滿了的緩存區(qū)域去請求一個對象的時(shí)候,下面的步驟就會執(zhí)行,這些步驟會在圖11中顯示:

      1.?程序請求一個對象?
      2.?緩存檢測這個對象是否已經(jīng)存在于緩存中?
      3.?緩存決定把一個對象開除出緩存(一般采用的算法是遺棄最近使用次數(shù)最少的對象)?
      4.?把這個對象扔出緩存(稱為passivated)?
      5.?把從數(shù)據(jù)庫中裝載這個新對象并放入到緩存(稱為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.

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

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

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

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

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

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

      1.?事務(wù)提交?
      2.?事務(wù)回滾
      當(dāng)一個事務(wù)被提交的時(shí)候,說明它已經(jīng)完全成功了,但是當(dāng)它回滾的時(shí)候,就說明發(fā)生了一些錯誤。回滾會是下面2種情況:

      1.?程序造成的回滾(程序回滾)?
      2.?非程序造成的回滾(非程序回滾)
      通常,程序回滾是因?yàn)樯虡I(yè)的規(guī)定。比如一個WEB程序做一個素描畫的價(jià)格的調(diào)查,程序可能讓用戶輸入年齡,并且商業(yè)規(guī)定18歲以上才可以進(jìn)入。如果一個16歲的提交了信息,那么程序就會拋出一個錯誤,打開一個網(wǎng)頁告訴他,他年齡還不能參與到這個信息的調(diào)查。因?yàn)槌绦驋伋隽水惓#虼税诔绦蛑械氖聞?wù)的就會發(fā)生回滾。這只是普通的程序回滾,只有當(dāng)發(fā)生大量的程序回滾才值得我們注意。

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

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

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

      總結(jié)

      盡管各個程序和他們的環(huán)境都各不相同,但是有一些共同的問題困擾著他們。這篇文章的注意力并不是放在程序代碼的問題上,因?yàn)榘炎⒁饬Ψ旁谝驗(yàn)榄h(huán)境的問題而導(dǎo)致的低性能的問題上:

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

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

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产AV无码专区亚洲AV手机麻豆| 91成人免费观看| 黄色三级三级三级免费看| 国产精品亚洲lv粉色| 亚洲av成人一区二区三区在线播放| 亚洲熟女综合色一区二区三区 | 午夜成人免费视频| 成人毛片免费观看视频| 97视频热人人精品免费| 免费大片黄在线观看yw| 国产在线国偷精品产拍免费| 97无码免费人妻超级碰碰夜夜| 成人免费看吃奶视频网站| 日韩免费一区二区三区| 亚洲av日韩片在线观看| 久久久久亚洲精品天堂久久久久久 | 一区二区免费在线观看| 一级女性全黄久久生活片免费 | 亚洲国产精品成人久久久| 亚洲专区中文字幕| 亚洲精品乱码久久久久蜜桃| www亚洲精品久久久乳| 一级毛片a免费播放王色| 免费无码又爽又刺激网站| 少妇人妻偷人精品免费视频| 成人浮力影院免费看| 免费无码又爽又刺激高潮的视频| 免费v片视频在线观看视频| 久久亚洲中文字幕精品一区| 亚洲国产精品久久久久久| 亚洲AV无码精品蜜桃| 国产精品亚洲精品爽爽| 两性色午夜视频免费播放| 日本一卡精品视频免费| 成人性生免费视频| 亚洲中文字幕无码爆乳av中文| 亚洲色图在线观看| 亚洲精品人成网线在线播放va| 久久免费香蕉视频| 波多野结衣在线免费视频| 又黄又爽的视频免费看|