第八章 Dispatching Requests
當(dāng)建立一個web應(yīng)用時,把一個請求傳給另一個servlet或在response中包含另一個servlet的輸出是經(jīng)常
使用的。RequestDispatcher接口就提供了一些方法。
8.1 獲得RequestDispatcher
實(shí)現(xiàn)了接口RequesetDispatcher接口的對象可以在ServletContext的getRequestDispatcher或
getNamedDispatchcer方法得到。
getRequestDispatcher的參數(shù)是一個以根目錄‘/’開始的一個路徑,該方法會查找路徑下的servlet,并把
它封裝成RequestDispatchcer對象返回。
getNamedDispatcher方法把一個ServletContext知道的servlet名字作為參數(shù),如果找到servlet,該
servlet就被封裝成RequestDispatcher對象返回,如果沒有找到則返回null。
RequestDispatcher對象中使用相對路徑也是可以的。在ServletRequest中提供了getRequestDispatcher
方法;這個方法和ServletContext中同名的方法功能類似。servlet引擎會用request的信息把相對路徑轉(zhuǎn)
化成完整路徑的。如ServleltRequest.getRequestDispatcher("header.html")和ServletConext.
getRequestDispatcher("/garden/headere.html")是等效的。
8.1.1 在Request Dispatcher 路徑中附加字符串
在ServletContext和ServletRequest創(chuàng)建RequestDispatcher方法中參數(shù)都可以帶字符串如:
Context.getRequestDispatcher("/raisons.jsp?orderno=5");
8.2 Request Dispatcher的使用
對于使用Request Dispatcher 而言就是一個servlet調(diào)用include或forward方法,這些方法的參數(shù)是
Servlet接口傳來的request和response對象實(shí)例。引擎必須確保調(diào)用Request Dispatcher的處理過程是在
同一個JVM的同一個線程中。
8.3 include 方法
RequestDispatcher接口的include方法可以在任何時候被調(diào)用;目標(biāo)servlet可以包含所有外的request對
象,不過response對象的使用是有限制的:
response只能寫信息到ServletOutputStream 或者Writer中,調(diào)用response對象的flushBuffer方法進(jìn)行提
交。不能夠設(shè)置頭信息,任何方法都不會影響到response的頭信息。
8.3.1 被包含的request參數(shù)
除了可以用getNamedDispatcher方法包含一個servlet外,以下的屬性可以被設(shè)置:
Java.servlet.include.request_uri
Java.servlet.include.context_path
Java.servlet.include.servlet_path
Java.servlet.include.path_info
Java.servlet.include.query_string
用request對象的getAttribute方法可以獲取被包含servlet的以上屬性。
如果被包含的servlet能后通過getNamedDispatcher方法找到就不必設(shè)置以上屬性了。
8.4 Forward 方法
RequestDispatcher接口中的forward方法,只有servlet沒有提交響應(yīng)到客戶端時才可用;如果響應(yīng)
buffer中有數(shù)據(jù)還沒有提交,當(dāng)調(diào)用forward方法中目標(biāo)servlet的service方法前,buffer中的內(nèi)容會被清
空;如果buffer中的數(shù)據(jù)提交了,則發(fā)生IllegalStateException錯誤。
request對象的路徑必須放映獲取RequestDispatcher對象的路徑。
有個例外,如果RequestDispatcher是通過getNamedDispatcher方法得到的,request對象必須反映原始
request的路徑。
在RequestDispatcher接口方法forward返回前,response的內(nèi)容必須被提交,并由引擎關(guān)閉該servlet。
8.4.1 query String
在Request Dispatcher中創(chuàng)建的路徑是可以帶參數(shù)的。
8.5 錯誤
如果request Dispatcher的目標(biāo)servlet拋出運(yùn)行時錯誤或ServletException 或IOException,錯誤就會被
傳給調(diào)用的servlet;在上傳之到調(diào)用的servlet之前,所有其他的exception都應(yīng)該包裝成
ServletExceptions。
第九章 web 應(yīng)用
一個web應(yīng)用是一堆servlet,html頁面,類和其他資源的集合。web應(yīng)用可以被發(fā)布運(yùn)行在很多服務(wù)提
供商的多種引擎下。
9.1 web服務(wù)器
在web服務(wù)中一個web應(yīng)用的根目錄是一個特殊的路徑,例如:一個網(wǎng)站目錄可以以http://www.
mycorp.com/登錄,所有的請求都將以這個作為前綴發(fā)送到以這個前綴描述的servletContext環(huán)境中。
在任何時候一個web應(yīng)用的實(shí)例只能運(yùn)行在一個JVM中。
9.2 和servletContext的關(guān)系
servlet引擎會強(qiáng)迫web應(yīng)用和ServletContext的通信,一個ServletContext對象提供了一個servlet使得該
應(yīng)用可見。
9.3 web應(yīng)用中的元素
一個web應(yīng)用包含以下的元素:
.Servlets
.JSP
.Utility Classes
.Static documents(html,images,sounds,etc)
.Client side Java applets,beans,and classes
.Descriptive meta informateion
9.4 部署層次
這個協(xié)議定義了一個層次結(jié)構(gòu),用于部署和打包,這個結(jié)構(gòu)存在于一個文件中。
9.5 目錄結(jié)構(gòu)
一個web應(yīng)用存在一個目錄層次結(jié)構(gòu)。文件根目錄是應(yīng)用的一部分。例如:一個web應(yīng)用的上下文路徑
是/catalog,web應(yīng)用的index.html文件就能被/catalog/index.html請求訪問。URL和上下文路徑的匹配
規(guī)則將在11章討論。servlet引擎必須拒絕一個具有現(xiàn)在沖突的上下文路徑的web應(yīng)用,這種情況是有
的,如:兩個web應(yīng)用發(fā)布在同一個上下文路徑中,或一個web應(yīng)用的上下文路徑是另一個web應(yīng)用上
下文路徑的子路徑。
有一個特殊的目錄(“WEB-INF”)在應(yīng)用中存在,這個目錄包含所有與應(yīng)用相關(guān),又不在根目錄中的事
物。可以直接被引擎提供給客戶端的文件不放在WEB-INF中,但WEB-INF目錄對于調(diào)用ServletContext
的getResource和getResourceAsStream方法的servlet 代碼是有效的。如果開發(fā)者想用servlet代碼調(diào)用
一個不希望暴露給客戶端的一個配置信息,就可以把這個配置信息放在WEB-INF目錄下。請求都是和資
源相匹配的;敏感的匹配如客戶端的請求是“/WEB-INF/foo”和“/Web-iNf/foo”,但不應(yīng)該把定位于/
WEB-INF下的內(nèi)容作為結(jié)果返回。
WEB-INF目錄下的內(nèi)容有:
./WEB-INF/web.xml 部署描述文件
./WEB-INF/classes/ 存放servlet class
./WEB-INF/lib/*.jar 是jar包的目錄
應(yīng)用的classloader先load WEB-INF/classes目錄下的class后load WEB-INF/lib目錄下的jar包
9.5.1 目錄結(jié)構(gòu)的一個例子
一個簡單web應(yīng)用的目錄結(jié)構(gòu):
/index.html
/howto.jsp
/images/banner.gif
/images/jumping.gif
/WEB-INF/web.xml
/WEB-INF/lib/jspbean.jar
/WEB-INF/classes/com/mycorp/servlets/MyServlet.class
/WEB-INF/classes/com/util/MyUtils.class
9.6 web應(yīng)用的存檔文件
一個web應(yīng)用可以被java打包工具打包成war文件,當(dāng)被打包后包中就會有一個額外META-INF目錄,該
目錄下存放了打包工具的一些信息。
9.7 web應(yīng)用部署描述
下面是web應(yīng)用部署描述中的配置類型:
.ServletContext Init Parameters
.Session Configuration
.Servlet / JSP Definitions
.Servlet / JSP Mappings
.MIME Type Mappings
.Welcome File list
.Error Pages
.Security
9.7.1 可靠的擴(kuò)展
web容器須提供一種機(jī)制使得web應(yīng)用知道jar文件中包含的有用資源或代碼。
引擎因該提供編輯、配置庫文件的程序。
在WAR中提供一個MANIFEST.MF文件,描述擴(kuò)展名列表是比較好的。標(biāo)準(zhǔn)的JAR是應(yīng)該有的,這個文件
描述的擴(kuò)展名應(yīng)該遵循Http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html中的規(guī)
定。web容器應(yīng)該能夠識別WEB-INF/lib文件夾中的任何文件的擴(kuò)展名,如果不能夠識別就應(yīng)該拒絕該
應(yīng)用程序,并報(bào)出錯誤。
9.7.2 web應(yīng)用的classloader
引擎用于裝載war中的servlet的裝載器必須能夠讓開發(fā)者裝載jar庫中的任何資源。但裝載的資源禁止覆
蓋j2se或servlet API中的類;通常建議的做法是裝載器不允許war中的servlet去訪問web引擎中的類。
還有一個被推薦的做法是實(shí)現(xiàn)應(yīng)用類裝載器,war中被裝載的類或資源就會被放到container-wide JAR庫
的特定類或資源中。
9.8 替換web應(yīng)用
一個服務(wù)器可能會在不重新啟動引擎的情況下用一個新版本的應(yīng)用替換原有的應(yīng)用。當(dāng)一個應(yīng)用被替換
時,引擎應(yīng)提供一個robust方法去保存該應(yīng)用中的session
9.9 錯誤句柄
9.9.1 request Attributes
web應(yīng)用必須列出在使用中資源發(fā)生的錯誤,這些資源在部署描述中都有定義。
如果錯誤在一個servlet或一個jsp頁面中發(fā)生,則在第9.1章中的如下的請求屬性就會被設(shè)置:
Request Attributes Type
Javax.servlet.error.status_code java.lang.Integer
Javax.servlet.error.exception_type java.lang.Class
Javax.servlet.error.message java.lang.String
Javax.servlet.error.exception java.lang.Throwable
Javax.servlet.error.request_uri java.lang.String
Javax.servlet.error.servlet_name java.lang.String
這些屬性允許這個servlet根據(jù)這些狀態(tài)碼、錯誤類型、錯誤信息、被拋出的錯誤對象、錯誤產(chǎn)生的
servlet被訪問的URI(可以用getRequestURI得到)、或錯誤產(chǎn)生的servlet的邏輯名稱產(chǎn)生特殊的內(nèi)
容。
在2.3版本中錯誤類型和錯誤信息屬性是多余的,他們被保留只是為了向下兼容以前的版本。
9.9.2 錯誤頁面
當(dāng)一個servlet產(chǎn)生錯誤時,開發(fā)者可以訂制錯誤內(nèi)容返回給客戶端。部署描述文件定義了一個錯誤頁面
列表。servlet在response中設(shè)置錯誤狀態(tài)碼或產(chǎn)生的異常或錯誤被引擎支持時,引擎就會從部署描述文
件中調(diào)用相應(yīng)的配置的錯誤資源。
如果一個錯誤碼被設(shè)置在了response中,引擎在部署描述文件的錯誤頁面列表中用status-code方式匹配
對應(yīng)的資源,如果找到就調(diào)用本地的資源。
在一個請求被處理的過程中servlet可以拋出以下的異常:
.runtime exceptions or errors
.ServletExceptions or subclasses thereof
.IOException or subclasses thereof
web應(yīng)用可以用exception-type元素來描述錯誤頁面,在這種情況下引擎會通過比較用exception-type元
素定義的error-page列表中的異常來匹配產(chǎn)生的異常。匹配的結(jié)果是返回定義的與錯誤匹配的本地資
源。在繼承類中,最近的類將被調(diào)用。
如果沒有一個error-page包含的exception-type與class-heirarchy相匹配。拋出的ServletException或其子
類異常,被引擎通過ServletException.getRootCause方法獲得,獲得后用這個異常再去配置的error
page列表中去匹配。
在部署描述文件中用exception-type元素定義的Error-page中exception-type的類名必須是唯一的。
當(dāng)錯誤發(fā)生在servlet調(diào)用的RequestDispatcher中時error page機(jī)制是不能夠干預(yù)到的;這樣的情況如:
一個servlet用RequestDispatcher去調(diào)用另一個有錯誤的servlet。
如果一個servlet產(chǎn)生的錯誤沒有被描述的錯誤頁面機(jī)制所抓到,引擎必須設(shè)置response的狀態(tài)碼為500
9.10 Welcome Files
web應(yīng)用可以在部署描述文件中定義一個welcome files調(diào)用的URI列表,這個機(jī)制的目的是允許開發(fā)者
定義自己的訪問首頁。
如果沒有在部署描述中配置welcome 文件,引擎將把局部請求(沒有指明具體訪問資源,如www.cacolg.
com/index.html,請求訪問時用www.cacolg.com/訪問的)發(fā)送到適當(dāng)?shù)馁Y源中,如:一個可能默認(rèn)的
servlet,或列出該目錄下的文件列表,或返回404響應(yīng)錯誤。
一個例子:
1)在部署描述中定義index.html和default.jsp為welcome files
2)定義一個servlet的mapping路徑為/foo/
WAR中有的文件如下:
/foo/index.html
/foo/default.html
/foo/orderform.html
/foo/home.gif
/catalog/default.jsp
/catalog/products/shop.jsp
/catalog/products/register.jsp
3)請求的URI為 處理后的URI
/foo 或 /foo/ /foo/index.html
/catalog/ /catalog/default.jsp
/catalog/index.html 404 not found
/catalog/products/ 404 not found 也可能返回shop.jsp and /or register.jsp 列表。
9.11 web應(yīng)用環(huán)境
j2EE定義的命名環(huán)境能夠使得應(yīng)用在不需要知道外部信息怎么命名的情況下比較方便的訪問資源或外部
信息。
servlet作為j2EE完整的一部分,使web應(yīng)用部署描述文件提供了一個servlet可以訪問資源和EJB,這些
部署描述元素有:
.env-entry
.ejb-ref
.ejb-local-ref
.resource-ref
.resource-env-ref
開發(fā)者使用這些元素描述web應(yīng)用中需要用到的對象,這些對象都要在web容器運(yùn)行時注冊到JNDI命名
空間。
在J2EE1.3版本j2EE的環(huán)境需求中,servlet引擎不是J2EE技術(shù)的一部分,web環(huán)境要提供的功能在J2EE
規(guī)范中有描述。如果沒有實(shí)現(xiàn)支持環(huán)境所要提供的功能,在發(fā)布應(yīng)用時,web容器就會拋出警告。
實(shí)現(xiàn)servlet引擎在J2EE中是需要的,應(yīng)該被納入J2EE1.3中。J2EE1.3應(yīng)該提供更多的內(nèi)容。
servlet引擎必須支持對象的lookup方法,查找對象并在引擎控制的線程中實(shí)例化。
servlet引擎應(yīng)該支持開發(fā)者創(chuàng)建的線程,因?yàn)閼?yīng)用創(chuàng)建的線程不是很輕便,開開發(fā)者不得不依賴于這些
功能有限的線程。這些需求將被加入到下一個版本的servlet規(guī)范中。
第十章 應(yīng)用周期事件
10.1 介紹
事件是servlet2.3種新添的內(nèi)容。應(yīng)用事件使得web開發(fā)者能夠控制ServletContext和HttpSession對象的
信息交互,使得管理web使用的資源更有效,方便。
10.2 事件監(jiān)聽器
事件監(jiān)聽器是實(shí)現(xiàn)了servlet事件監(jiān)聽接口的類。在web發(fā)布是這些監(jiān)聽類就被實(shí)例化和注冊在web容器
中。
servlet事件監(jiān)聽器提供了在ServletContext和HttpSerssion對象狀態(tài)發(fā)生改變時觸發(fā)的事件。Servlet
cotext監(jiān)聽器用于管理應(yīng)用的資源或虛擬機(jī)的狀態(tài)。HTTP session監(jiān)聽器管理與會話關(guān)聯(lián)的資源。
可以有多個監(jiān)聽器監(jiān)聽每一個事件類型。開發(fā)者可以指定引擎調(diào)用監(jiān)聽類的順序。
10.2.1 事件類型和監(jiān)聽接口
Event Type ListenerInterface 說明
Lifecycle javax.servlet.ServletContextListener 當(dāng)servlet context被創(chuàng)建并有效的
接受第一個請或servlet context銷毀前
Changes to attributees javax.servlet.ServletContextAttributesListener 當(dāng)servlet context中的屬性發(fā)生
added,removed,replaced
Lifecycle javax.servlet.http.HttpSessionListener 當(dāng)HttpSession被創(chuàng)建,無效或超時
Changes to attributes javax.servlet.HttpSessionAttributesListener 當(dāng)屬性added,removed或replaced時
10.2.2 一個使用監(jiān)聽的例子
一個簡單的web應(yīng)用中有servlet要訪問數(shù)據(jù)庫,開發(fā)者提供一個context 監(jiān)聽類管理數(shù)據(jù)庫連接。
1)web應(yīng)用啟動時,監(jiān)聽類被裝載,登陸數(shù)據(jù)庫,在servlet context中保存數(shù)據(jù)庫連接。
2)servlet訪問數(shù)據(jù)庫連接
3)當(dāng)web服務(wù)銷毀時,或應(yīng)用從web服務(wù)中刪除時,關(guān)閉數(shù)據(jù)庫連接。
10.3 監(jiān)聽類的配置
10.3.1 對監(jiān)聽類的規(guī)定
web開發(fā)者提供實(shí)現(xiàn)了以上監(jiān)聽接口的類,每個類應(yīng)該有一個沒有參數(shù)的構(gòu)造器函數(shù)。監(jiān)聽類放在
WEB-INF/classes下或以一個jar文件放在WEB-INF/lib下都可以。
10.3.2 部署描述
web容器對每個監(jiān)聽類只會創(chuàng)建一個實(shí)例,在第一個請求到來之前實(shí)例化并注冊。web容器注冊監(jiān)聽類
的順序根據(jù)他們實(shí)現(xiàn)的接口和在部署描述文件中定義的順序。web應(yīng)用調(diào)用監(jiān)聽實(shí)例的順序按照他們注
冊的順序。
10.3.4 在銷毀時的事件
當(dāng)應(yīng)用銷毀時監(jiān)聽事件的執(zhí)行順序按部署描述中的順序,先執(zhí)行session中的監(jiān)聽事件再執(zhí)行context中
的監(jiān)聽事件。session的無效事件必須在context的銷毀事件之前被調(diào)用。
10.4 部署描述的例子
下面給出注冊兩個servlet cocntext lifecycle監(jiān)聽器和一個HttpSession監(jiān)聽器的例子。
Com.acme.MyconnectionManager和com.acme.MyLoggingMoudule都實(shí)現(xiàn)了javax.
servletServletContextListener接口,com.acme.MyloggingModule另外還實(shí)現(xiàn)了javax.servlet.
HttpSessionListener接口。開發(fā)者希望com.acme.MyConnectionManager在com.acme.
MyLoggingModule之前管理者servlet context 的生命周期事件。部署描述文件如下:
<web-app>
<display-name>MyListeningApplication</display-name>
<listener>
<listener-class>com.acme.MyConnectionManager</listener-class>
</listenrer>
<listenrer>
<listenrer-class>com.acme.MyLoggingModele</listener-class>
</listener>
<sevlet>
<display-name>RegistrationServlet</display-name>
..etc
</servlet>
</web-app>
10.5 監(jiān)聽器的實(shí)例和線程
在第一個請求被web容器接受之前實(shí)例化并注冊好監(jiān)聽器類是必須的。監(jiān)聽器在整個web應(yīng)用生命周期
中都要使用。
ServletContext和HttpSession對象屬性的改變可能會同時產(chǎn)生,引擎不需要同步這些屬性類的事件。
10.6 分布式容器組
在分布式web容器組中,HttpSession和ServletContext實(shí)例只活動與它們本地的JVM中。在分布式web容
器中,監(jiān)聽實(shí)例會在每一個web容器中創(chuàng)建實(shí)例。
10.7 session事件
監(jiān)聽器使得開發(fā)者可以跟蹤web應(yīng)用中的session。知道session是否變得無效是經(jīng)常被用到的,因?yàn)?br>session超時時引擎會使session變得無效,或應(yīng)用會調(diào)用invalidate方法。