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