http://www.uml.org.cn/j2ee/j2ee003.htm J2EE組件間共享對(duì)象技術(shù) |
作者:龔永生 本文選自:開(kāi)放系統(tǒng)世界—賽迪網(wǎng) 2003年02月26日 |
想要用好Struts應(yīng)用框架,必須了解J2EE Web級(jí)JSP和Servlet技術(shù)存放共享對(duì)象的幾種方式。同時(shí),要利用J2EE 開(kāi)發(fā)Web應(yīng)用程序也必須掌握組件間對(duì)象共享的機(jī)制。 像Java程序有類級(jí)別變量、方法級(jí)別變量一樣,J2EE Web應(yīng)用程序有四個(gè)對(duì)象存放共享對(duì)象。這些共享對(duì)象存放在那里,以便存放者或者其它程序代碼日后使用。這四個(gè)對(duì)象分別是頁(yè)面、請(qǐng)求、會(huì)話和應(yīng)用程序,它們都是以數(shù)據(jù)結(jié)構(gòu)鍵/值對(duì)的形式保存的。同時(shí)這四個(gè)對(duì)象形成了四個(gè)級(jí)別的共享對(duì)象存放地,即應(yīng)用程序?qū)ο笾械墓蚕韺?duì)象是全局性的,在整個(gè)應(yīng)用程序的生命周期內(nèi)有效(當(dāng)然主動(dòng)去掉除外),屬于所有的上網(wǎng)用戶;會(huì)話對(duì)象中的共享對(duì)象是在一個(gè)會(huì)話期內(nèi)有效,屬于用戶的當(dāng)前會(huì)話;請(qǐng)求對(duì)象中的共享對(duì)象在一個(gè)請(qǐng)求期內(nèi)有效,屬于用戶發(fā)送的當(dāng)前請(qǐng)求;頁(yè)面對(duì)象中的共享對(duì)象只屬于當(dāng)前頁(yè)面的執(zhí)行實(shí)例。本文主要分析共享對(duì)象的設(shè)置和訪問(wèn)方法,包括共享對(duì)象的有效范圍、具體訪問(wèn)方法、輔助顯示手段和多線程下的實(shí)現(xiàn)策略。 Servlet運(yùn)行時(shí)已經(jīng)準(zhǔn)備好了這些范圍對(duì)象,如表1所示。
Servlet中的共享對(duì)象如表2。
在JSP技術(shù)中,有兩種把資源片斷組合成一個(gè)資源的技術(shù):include 指示符和jsp:include元素。指示符的語(yǔ)法為:
當(dāng)一個(gè)JSP被翻譯成Servlet時(shí),它會(huì)被處理。jsp:include元素的語(yǔ)法是:
當(dāng)這個(gè)JSP頁(yè)面被執(zhí)行時(shí),它會(huì)被處理。指示符是代碼的組合,元素則是結(jié)果的組合。fragmentresource.jsp和主頁(yè)面具有一樣的上下文,如頁(yè)面對(duì)象;而included.jsp不具有和主頁(yè)面一樣的頁(yè)面對(duì)象,但請(qǐng)求對(duì)象是同一個(gè)。 在Servlet中,RequestDispatcher.include(request,response)實(shí)現(xiàn)結(jié)果的整合,示例代碼如下:
在利用RequestDispatcher.forware(request,response)把控制傳給另一個(gè)Web組件設(shè)置形成一個(gè)控制管道時(shí),要嚴(yán)格遵循“前面的組件處理request,最后的組件處理response”的準(zhǔn)則。前面的組件甚至不能企圖獲取response輸出流的引用。這個(gè)控制管道中的組件具有同一個(gè)request對(duì)象,不具有相同的pageContext對(duì)象(針對(duì)JSP)。Servlet中傳遞控制的例子如下:
JSP中傳遞控制的例子如下:
在JSP中,可以通過(guò)jsp:param元素來(lái)增加請(qǐng)求對(duì)象的參數(shù),適用于jsp:include和jsp:forward兩元素。 示例如下:
![]() 在網(wǎng)頁(yè)上顯示各個(gè)級(jí)別的共享對(duì)象是一個(gè)非常不錯(cuò)的調(diào)試手段。下面的顯示共享對(duì)象類圖(如圖1)實(shí)現(xiàn)了這個(gè)功能。它以類org.i18.struts.AttributeUtils為核心,這個(gè)類負(fù)責(zé)把四個(gè)對(duì)象的共享對(duì)象保存為鍵/值對(duì)的HashMap對(duì)象。通過(guò)它可以得到請(qǐng)求對(duì)象中的共享對(duì)象及參數(shù)信息,以及頁(yè)面對(duì)象、會(huì)話對(duì)象、應(yīng)用對(duì)象這些共享對(duì)象的函數(shù)接口。在JSP頁(yè)面中,通過(guò)下面的代碼可以給這個(gè)類的對(duì)象提供輸入:
JSP頁(yè)面可以利用Struts提供的logic標(biāo)簽庫(kù)顯示這些共享對(duì)象:
在Servlet中,AttributeDisplayHelper幫助者類完成JSP中l(wèi)ogic標(biāo)簽庫(kù)相應(yīng)的功能。幫助者類完成工作后,AttrServlet把幫助者類完成的結(jié)果放在request中的一個(gè)共享屬性Attr中,接著把控制傳給servletAttr.jsp,再由它訪問(wèn)AttrServlet在request中設(shè)置的共享屬性Attr,并顯示結(jié)果。 使用者可以通過(guò)attr.jsp、AttrServlet的url映射和index.jsp的提交按鈕來(lái)查看當(dāng)前上下文所有級(jí)別的共享對(duì)象。 J2EE系列規(guī)范中,EJB規(guī)范保證了組件開(kāi)發(fā)者在單線程的環(huán)境下編程,但Servlet規(guī)范沒(méi)有規(guī)定Servlet的系列服務(wù)方法在單線程模式下運(yùn)作,所以開(kāi)發(fā)者在使用共享對(duì)象時(shí)要注意線程同步問(wèn)題。一個(gè)比較通用的原則是:在一個(gè)專職的組件中設(shè)置共享對(duì)象,當(dāng)設(shè)置和訪問(wèn)破壞數(shù)據(jù)的一致性時(shí),使用Java的同步控制。 一個(gè)Servlet(包括JSP)的生命周期由其所在的容器控制。當(dāng)有一個(gè)Servlet請(qǐng)求時(shí),容器執(zhí)行如下步驟: 1.如果此Servlet的實(shí)例不存在,容器先裝載Servlet的類代碼,創(chuàng)建一個(gè)Servlet實(shí)例,接著調(diào)用這個(gè)實(shí)例的init方法。 2.如果此Servlet的實(shí)例存在,容器分配一個(gè)處理用戶請(qǐng)求的工作線程。如果Servler實(shí)現(xiàn)了SingleThreadModel接口,工作線程會(huì)在這個(gè)實(shí)例上同步,并且在取得訪問(wèn)權(quán)限后調(diào)用實(shí)例的service方法,否則直接調(diào)用實(shí)例的service方法。javax.servlet.http.HttpServlet的service方法會(huì)根據(jù)用戶的HTTP請(qǐng)求類型調(diào)用相應(yīng)的doxxx方法。 3.只有當(dāng)所有的線程從這個(gè)實(shí)例中退出,容器在回收這個(gè)實(shí)例時(shí)才會(huì)調(diào)用這個(gè)實(shí)例的destroy方法。 Servlet實(shí)例線程圖(如圖2)體現(xiàn)了這個(gè)生命周期模型。 ![]() 雖然可以讓所有的Servlet實(shí)現(xiàn)SingleThreadModel接口,但這會(huì)嚴(yán)重影響程序的性能。要解決多線程的同步問(wèn)題,我們首先要分析共享對(duì)象的訪問(wèn)模式。在一個(gè)Web程序中,共享對(duì)象按訪問(wèn)模式可以分為以下兩類:一次設(shè)置、多次讀取的共享對(duì)象和多次設(shè)置、多次讀取的共享對(duì)象。 一次設(shè)置、多次讀取 對(duì)于這種共享對(duì)象,可以開(kāi)發(fā)一個(gè)事件監(jiān)聽(tīng)器,監(jiān)聽(tīng)程序啟動(dòng)和停止事件,代碼如下:
這樣,當(dāng)Web應(yīng)用程序啟動(dòng)時(shí),容器會(huì)調(diào)用監(jiān)聽(tīng)器,從而設(shè)置共享對(duì)象。共享對(duì)象的訪問(wèn)只需直接調(diào)用getAttribute方法即可。 多次設(shè)置、多次讀取 對(duì)于多次設(shè)置、多次讀取的共享對(duì)象,必須利用Java的同步機(jī)制,訪問(wèn)步驟如下: 第一步,首先設(shè)計(jì)一個(gè)同步類。這個(gè)同步類的代碼非常簡(jiǎn)單:
以上代碼可以看出,它其實(shí)什么都沒(méi)做,但繼承了Object關(guān)于同步的方法和機(jī)制。 第二步,把這個(gè)類的實(shí)例放到應(yīng)用對(duì)象中作為一個(gè)共享對(duì)象。可以看出這個(gè)同步對(duì)象屬于一次設(shè)置、多次使用的共享對(duì)象。在應(yīng)用程序啟動(dòng)事件監(jiān)聽(tīng)器中設(shè)置它,請(qǐng)參見(jiàn)前面的代碼。 第三步,設(shè)置共享對(duì)象。如果有對(duì)象需要整個(gè)應(yīng)用程序共享,可以在Servlet的service中利用同步機(jī)制來(lái)設(shè)置:
第四步,讀取共享對(duì)象。當(dāng)需要訪問(wèn)多次設(shè)置、多次訪問(wèn)的共享對(duì)象時(shí),同樣需要利用同步機(jī)制,代碼如下:
會(huì)話對(duì)象內(nèi)共享對(duì)象的多線程問(wèn)題可以和應(yīng)用對(duì)象一樣處理。請(qǐng)求對(duì)象和頁(yè)面對(duì)象正常情況下是線程安全的,除非開(kāi)發(fā)者自己引入了額外的線程。 本文分析了開(kāi)發(fā)好的J2EE應(yīng)用必須掌握的、組件間對(duì)象共享的技術(shù),這種分析技術(shù)同樣適用于EJB中。在EJB容器中,信息存放的位置變成了實(shí)現(xiàn)JNDI的服務(wù)提供者,大家通過(guò)JNDI的接口方法查詢、綁定共享對(duì)象。需要注意的是,同步對(duì)象不能在JNDI中實(shí)現(xiàn),因?yàn)榇蠹宜阉鞒鰜?lái)的不是同一個(gè)內(nèi)存對(duì)象。 |