淺淡 JSP 技術(shù)中使用最多的對象
級別:中級 |
Kyle Gabhart (kyle@gabhart.com)
顧問,Gabhart Consulting
2003年 9 月
接著上月對會話作用域的介紹,企業(yè) Java 專家 Kyle Gabhart 深入研究了 JSP 隱式對象的多種用法。接下來,他將介紹 9 個隱式對象,解釋每個對象的用途(或者多種用途),最后給出一些怎樣在 JSP 編程中使用這些便利工具的最佳實踐。您可以到我們的 討論論壇 中分享您對這篇文章或者 J2EE 探索者 系列中的任何其他文章的想法。
本期的 J2EE探索者 是上個月的 正確處理會話作用域入門 的續(xù)篇。除了訪問會話作用域之外,JSP 隱式對象還可以用來處理 HTML 參數(shù),轉(zhuǎn)發(fā)請求到一個 Web 組件,包括組件的內(nèi)容、通過 JSP 容器的日志數(shù)據(jù)、控制輸出流,處理異常,等等。
本月,您將學(xué)到在 JSP 頁面中使用隱式對象。我們首先簡要概括 JSP 架構(gòu),其中包括了隱式對象。然后,我將介紹每個對象并描述它的核心功能。最后,我們將給出使用每種類型的對象和它提供的容器管理服務(wù)的一些最佳實踐。
隱式對象簡介
JSP 架構(gòu)背后的理念是提供一個 Web 組件,它允許開發(fā)人員著重關(guān)注 Web 內(nèi)容的表示,而不用陷入解析、編程和數(shù)據(jù)操縱等細(xì)節(jié)。JSP 應(yīng)用程序本質(zhì)上是特殊的 Web 組件,在處理用戶請求之前,J2EE Web 容器首先將其轉(zhuǎn)換成 servlet。在每個 JSP 應(yīng)用程序內(nèi)部有一套完整的隱式對象。
隱式對象使得開發(fā)人員可以訪問容器提供的服務(wù)和資源。這些對象之所以定義為隱式的,是因為您不必顯式地聲明它們。不論您是否聲明它們——雖然您不能重復(fù)聲明它們,它們在每個 JSP 頁面當(dāng)中都進(jìn)行定義,并且在后臺由容器使用。因為隱式對象是自動聲明的,所以我們只需要使用與一個給定對象相關(guān)的引用變量來調(diào)用其方法。
9 個隱式對象及其功能的簡單描述如下:
Application
Config
Exception
include 含有只能由指定的 JSP“error pages”訪問的異常數(shù)據(jù)。
Out
提供對 servlet 的輸出流的訪問。
Page
PageContext
Request
Response
Session
雖然有些隱式對象只提供單一的功能,但是幾個結(jié)合起來使用就可以提供多種功能。在接下來的一節(jié)里,我們將按照功能分類來考察隱式對象:
- 會話管理:
application
,session
,request
,pageContext
- 流控制:
application
,config
,pageContext
,request
,session
- 日志記錄和異常:
application
,config
,exception
,pageContext
,request
,session
- 輸入/輸出控制:
request
,response
,out
- 初始化參數(shù):
config
會話管理
上個月我們提到過,為 JSP 定義的四個隱式對象可以用來在一個特定的上下文或者作用域中加入有狀態(tài)數(shù)據(jù)。這四個對象是 application
、session
、request
和 pageContext
。下表列出了這四個對象和它們定義的狀態(tài)上下文,同時還給出了對每個對象的簡單描述。
表1. JSP 狀態(tài)管理
隱式對象 | 作用域 | 描述 |
javax.servlet.ServletContext | Application |
代表整個運行時的 Web 模塊(應(yīng)用程序)。作用域為 application 的數(shù)據(jù)在同一個應(yīng)用程序模塊的所有 Web 組件之間共享。這很像J2EE 中提供的“全局(global)”數(shù)據(jù) |
javax.servlet.http.HttpSession | Session |
代表當(dāng)前的 HTTP 會話。除 page 作用域外,session 作用域是使用最普遍的上下文。這個對象在提供跨多個請求的持久的、有狀態(tài)的用戶體驗方面使用得最普遍 |
javax.servlet.http.HttpServletRequest | Request |
代表當(dāng)前的 HTTP 請求。這個上下文可以跨越多個 Web 組件(servlet 和 JSP 頁面),只要這些組件屬于同一原子請求的一部分。由客戶機提供的特定于請求的數(shù)據(jù)(請求方法、URI、HTTP 參數(shù)等等)都被自動地保存在一個request 上下文中。servlet 或 JSP 頁面還可以程式化地(programmatically)將數(shù)據(jù)的作用域指定為 request ,以便允許同一 request 作用域中的其他 servlet 或 JSP 頁面可以獲取該數(shù)據(jù) |
javax.servlet.jsp.PageContext | Page |
代表當(dāng)前 JSP 頁面的上下文。因為一個 JSP 頁面的上下文包括當(dāng)前的請求、會話和應(yīng)用程序,所以使用pageContext 實例可以訪問與一個JSP 頁面相關(guān)的所有命名空間。它是所有對象的默認(rèn)作用域,包括 JavaBeas 對象在內(nèi)。 具有 page 作用域的對象通常會綁定到一個局部變量,以便在 scriptlet、表達(dá)式、JavaBeans 標(biāo)記和自定義標(biāo)記中可以訪問它 |
從最佳實踐的立場來看,我們應(yīng)該盡可能地使用 page
作用域。它簡單,而且是 JSP 數(shù)據(jù)的默認(rèn)作用域。request
作用域非常適合于運行期間在組件間共享數(shù)據(jù)以處理一個特定的請求。session
作用域被設(shè)計用來為單個用戶提供持久的、有狀態(tài)的體驗,它可以跨越多個請求。application
作用域只有需要在組件之間跨用戶會話共享數(shù)據(jù)時才應(yīng)該使用。參閱參考資料以了解更多有關(guān) session 作用域的信息。
流控制
面向?qū)ο笤O(shè)計方法的最大好處是可重用性。特別是,J2EE 系統(tǒng)將它們借用到模塊化風(fēng)格的開發(fā)中,其中組件可以在其他應(yīng)用程序中重新安排、重新打包和重新使用。即使您對設(shè)計可重用的 Web 模塊不感興趣,也很可能會發(fā)現(xiàn)您的 J2EE 應(yīng)用程序由幾個部分組成。任何時候使用多個 servlet 或者 JSP 頁面(也就是組件)完成一個請求的時候,都需要使用某種類型的流控制技術(shù)。Servlet 架構(gòu)提供兩種這樣的技術(shù):forward(轉(zhuǎn)發(fā)) 和 include(包括)。
在 J2EE Web 開發(fā)中,forward 會把處理用戶請求的控制權(quán)轉(zhuǎn)交給到其他 Web 組件。forward 在有些時候會比較有用,比如說需要用一個組件設(shè)置一些 JavaBean、打開或關(guān)閉資源、認(rèn)證用戶,或者在將控制權(quán)傳遞給下一個組件之前需要執(zhí)行一些準(zhǔn)備工作。在轉(zhuǎn)發(fā)之前可以執(zhí)行很多類型的任務(wù),但是要轉(zhuǎn)發(fā)的組件不能設(shè)置響應(yīng)頭部信息,也不能有內(nèi)容發(fā)送到輸出緩沖區(qū)。所有與向客戶發(fā)送內(nèi)容直接相關(guān)的任務(wù)必須由被轉(zhuǎn)發(fā)的組件完成。
J2EE 中第二種流控制技術(shù)是include。在使用 forward 時,要傳遞控制權(quán)。與此不同的是,執(zhí)行 include 的組件維持對請求的控制權(quán),而只是簡單地請求將另一個組件的輸出包括在該頁面的某個特定的地方。對于常見的設(shè)計元素,例如頁首、頁腳和導(dǎo)航欄等,這是一個非常好的方法。
forward 和 include 都是通過一個專門的對象 java.servlet.RequestDispatcher
來完成的。簡單地調(diào)用一個 ServletContext
對象的 getRequestDispatcher()
方法就可以獲得一個RequestDispatcher
對象。得到對 ServletContext
對象的引用有幾種方法,我們可以:
- 使用隱式聲明的
application
ServletContext。
- 調(diào)用方法
getServletContext()
,該方法返回一個對隱式聲明的 application 變量的引用。 - 調(diào)用隱式聲明的
config
etServletContext()
方法。
- 調(diào)用隱式聲明的
pageContext
getServletContext()
方法。
- 調(diào)用隱式聲明的
request
變量的getServletContext()
方法。
- 調(diào)用隱式聲明的
session
getServletContext()
方法。
清單1給出了使用隱式變量 application
的 forward 流控制機制的代碼示例。
|
清單2給出了同樣使用變量 application
的 include 流控制的代碼示例。
|
forward 和 include 是添加到 J2EE Web 開發(fā)工具包中的兩個非常棒的技術(shù)。還有其他一些方法可以在 JSP 頁面中完成 include,而且還有很多解決 J2EE 設(shè)計模式方面的文獻(xiàn)中講到了如何結(jié)合使用這兩種技術(shù)。參閱參考資料以了解更多信息。
日志記錄和異常
如果您需要把與 Web 應(yīng)用程序相關(guān)的信息存儲到一個日志中,依然有內(nèi)建的方法可用。ServletContext
接口聲明了兩個方法,用于把數(shù)據(jù)傳給一個日志。其中一個方法接受簡單的文本消息:log( java.lang.String )
,另一個方法接受一個異常信息和一個文本消息:log(java.lang.Throwable, java.lang.String )
。
在有了 ServletContext
接口提供的兩個可用的日志記錄方法之后,剩下的關(guān)鍵是獲取一個對ServletContext
類型的對象的引用。像我們前面討論過的流控制對象一樣,有多種方法可以獲取對 ServletContext
類型的對象的引用。在獲得了對象引用之后,簡單地調(diào)用 log()
方法并向方法中傳遞必需的數(shù)據(jù)即可。一旦調(diào)用了這個方法,您當(dāng)然就會希望能夠查看應(yīng)用程序日志以查看消息。ServletContext
是一個簡單的接口,并且也沒有規(guī)定怎樣實現(xiàn)它聲明的方法。因而 log 方法的具體實現(xiàn)是由供應(yīng)商處理的。他們可以把日志信息存儲到一個文本文件、二進(jìn)制文件、數(shù)據(jù)庫中,或者是供應(yīng)商認(rèn)為合適的其他格式中。您需要從服務(wù)器的文檔中得知存儲日志的位置。
雖然向一個日志文件發(fā)送消息相當(dāng)有用,但是很多時候您可能還想在發(fā)生不可恢復(fù)的異常時顯示一個用戶友好的錯誤消息。要實現(xiàn)這一功能,您可以聲明,您的 JSP 頁面使用一個單獨的頁面來處理錯誤消息。這是在 JSP 頁面的任何地方通過包含下面的 page 指令實現(xiàn)的:
|
如果在處理 JSP 頁面時有一個異常拋出的話,exception 對象就會立即通過隱式聲明的 exception
變量的方式拋給指定的錯誤頁面。
為了使一個 JSP 頁面能夠作為一個錯誤頁面,它必須包含一個指令來聲明這個頁面是指定用來處理錯誤的特殊頁面,指令如下:
|
為了使用 ErrorMessage.jsp 頁面能夠作為一個錯誤頁面,這個指令必須出現(xiàn)在頁面的某個地方。錯誤頁面可以顯示一個友好的信息給用戶,然后可以將相關(guān)的異常信息寫入日志以供管理員日后查看。
輸入和輸出控制
因為 JSP 頁面僅僅是 HTTP servlet 的一個簡單抽象,所以您可以訪問 HttpServletRequest
和 HttpServletResponse
對象。如果需要特定于請求的信息,比如客戶機瀏覽器的類型、HTTP post 的內(nèi)容類型、客戶機性能、Cookie 數(shù)據(jù)或者請求參數(shù),簡單地用隱式聲明的 request
變量直接調(diào)用適當(dāng)?shù)姆椒纯伞n愃频兀绻枰O(shè)置響應(yīng)頭部信息,比如說瀏覽器類型、內(nèi)容類型、內(nèi)容長度等等,簡單地用隱式變量 response
調(diào)用適當(dāng)?shù)姆椒纯伞?
如果需要直接訪問 JSP 頁面的輸出流,您可能會試圖通過隱式 response
變量調(diào)用 getWriter()
或 getOutputStream()
。然而由于 JSP 頁面的特殊性,您不能這樣做。如果需要直接訪問輸出流,必須通過一個 avax.servlet.jsp.JSPWriter
類型的特殊緩沖 PrintWriter
對象來訪問。怎樣定位這樣一個對象的引用呢?JSP 容器將會為您隱式地聲明一個,并通過 out
變量提供給您。在 JSP scriptlet 中可以通過簡單地調(diào)用 out.print()
或 out.println()
使用它。
一般來說不需要像這樣直接使用 JSPWriter 對象,而只需簡單地把內(nèi)容作為普通文本或者通過 JSP 表達(dá)式寫入,然后允許容器將這些信息翻譯成 JSPWriter 調(diào)用。然而,在兩種情況下您需要直接使用 out
變量。一種情況是要為 JSP 自定義標(biāo)記定義處理程序,這部分內(nèi)容我們將在下個月重點講到。另外一種情況是您想要對 JSP 創(chuàng)建的輸出擁有更多的控制。如果您有一段夾雜著 JSP scriptlets 和表達(dá)式的 HTML,您可能會發(fā)現(xiàn)創(chuàng)建大的 scriptlet 然后在需要輸出內(nèi)容到客戶機的時候使用 out.println()
語句這樣做會更簡潔、更容易。
初始化參數(shù)
如果您有一些靜態(tài)數(shù)據(jù)想提供給 JSP 頁面使用,并且那些數(shù)據(jù)不會頻繁地改動,初始化參數(shù)可能會是一個比較好的選擇。初始化參數(shù)有時候又叫環(huán)境變量或者“init”參數(shù),這些參數(shù)通過位于一個 per-servlet/JSP 內(nèi)的 Web 應(yīng)用程序的 web.xml 文件指定,并且它們在servlet 的生命周期中只讀取一次,即在初始化時讀取。
清單3是一個初始化參數(shù)聲明的例子。
清單3. 初始化參數(shù)聲明
|
使用隱式變量 config
可以訪問這些參數(shù)的值,隱式變量 config
是對 JSP 頁面的ServletConfig
對象的引用。通過 ServletConfig
接口提供了兩個處理 init 參數(shù)的方法。可以根據(jù)名字對一個特定的參數(shù)完成一個查找(getInitParameter( java.lang.String)
), 或者也可以檢索到為 JSP 頁面定義的所有參數(shù)名字的一個 enumeration(getInitParameterNames()
)。在擁有了enumeration 之后,可以通過循環(huán)查找每一個值。所有 init參數(shù)都是String
對象。如果需要其他的數(shù)據(jù)類型,比如說整數(shù)、浮點數(shù)或者布爾值,必須使用相應(yīng)的包裝器類來解析字符串。
結(jié)束語
JSP 技術(shù)提供了 Servlet 架構(gòu)之上的一個非常有用的抽象,它讓 Web 設(shè)計者可以著重關(guān)注內(nèi)容表示,而只要求知道較少的編程細(xì)節(jié)。在本文中,您已經(jīng)看到了我們是如何使用隱式對象來快速、容易地開發(fā) Web 應(yīng)用程序的。
下個月,我們將開始講述 JSP 自定義標(biāo)記和 JSP 標(biāo)準(zhǔn)標(biāo)記庫(JSTL)。學(xué)習(xí)自定義標(biāo)記如何促進(jìn)表示和業(yè)務(wù)邏輯之間的分離,同時還讓您可以將動態(tài)數(shù)據(jù)合并到表示層。到那時,一起快樂地探索吧!
- 參考 Kyle Gabhar 撰寫的完整的J2EE探索者 系列。與本文特別關(guān)聯(lián)的文章是“創(chuàng)建和管理有狀態(tài) Web 應(yīng)用程序”。
- 要學(xué)習(xí)自定義標(biāo)記,請參考文章“使用自定義標(biāo)記控制 JSP 頁面”(developerWorks,2002年2月)。
- Mark Kolb 撰寫的詳盡的分為四部分的系列文章 A JSTL primer (developerWorks,2003年2-5月)示范了作用域變量、隱式對象和 JSP 自定義標(biāo)記如何在 JSP 頁面中協(xié)同工作。
- Brett McLaughlin 的 JSP最佳實踐 系列 是學(xué)習(xí)更多 JavaServer Pages 技術(shù)的一個非常好的資源。
- 要更全面地了解 JSP 技術(shù),您應(yīng)該閱讀 Noel Bergman 的教程“JSP 技術(shù)入門”(developerWorks,2001年8月)。
- 在 developerWorks Java 技術(shù)專區(qū)可以找到數(shù)百篇有關(guān) Java 編程各個方面的文章。
關(guān)于作者 ![]() |