Java Servlet規範

0.1. 誰應當閱讀本規範
本文檔的目標讀者爲:
 遵循規範提供Servlet引擎的Web服務器或應用服務器提供商
 Web應用開發工具提供商
 需要了解servlet運行機制,以編寫複雜的servlet應用的開發人員
說明:
本規範不是關於servlet的用戶手冊。
0.2. API參考
Java Servlet API Reference v2.2 提供了組成Servlet API的所有接口、類、例外(Exception)、方法的說明。本文檔中對各函數的參數簽名做了簡要說明。請參考API參考文檔瞭解詳細說明。
0.3. 其他的Java規範
在本規範中,將多處參考如下Java API規範:
 Java2 Platform Enterprise Edition v1.2(J2EE)
 JavaServer Pages v1.1(JSP)
 JavaNaming and Direcotry Interface(JNDI)
上述規範可以從J2EE網站上找到:
http://java.sun.com/j2ee
0.4. 其他重要的參考資料
在Servlet API和Servlet應用的開發實現中,還參考和遵循瞭如下互聯網規範:
 RFC 1945 Hypertext Transfer Protocol (HTTP/1.0)
 RFC 2045 MIME Part One: Format of Internet message Bodies
 RFC 2046 MIME Part Two: Media Types
 RFC 2047 MIME Part Three: Message Header Extensions for non-ASCII text
 RFC 2048 MIME Part Four: Registration Procedures
 RFC 2049 MIME Part Five: Conformance Criteria and Examples
 RFC 2109 HTTP State Management Mechanism
 RFC 2145 Use and Interpretation of HTTP Version Numbers
 RFC 2324 Hypertext Coffee Pot Control Protocol (HTCPCP/1.0)
 RFC 2616 Hypertext Transfer Protocol (HTTP/1.1)
 RFC 2617 HTTP Authentication: Basic and Digest Authentication
上述RFC文檔可以下面的網站上找到:
http://www.rfc-editor.org/
W3c協會(http://www.w3.org)是HTTP相關信息的權威發佈機構
在本規範中,部署描述符使用了XML(Extensible Markup Language)。在如下網站可以找到關於XML的更多信息:
http://java.sum.com
http://www.xml.org
0.5. 提供反饋
Java社區的成功有賴於您的積極參與。我們歡迎您就本規範提出任何方面的反饋意見,請將您的意見email到:[email protected]
由於會收到大量的反饋信息,我們的工程師無法對郵件一一進行回覆。但我們將安排一個專門小組,對所有的反饋信息進行認真地閱讀、評估和存檔。
0.6. 鳴謝
感謝Anselm Baird-Smith, Elias Bayeh, Vince Bonfanti, Larry Cable, Robert Clark, Daniel Coward, Satish Dharmaraj, Jim Driscoll, Shel Finkelstein, Mark Hapner, Jason Hunter, Rod McChesney, Stefano Mazzocchi, Craig McClanahan, Adam Messinger, Ron Monzillo, Vivek Nagar, Kevin Osborn, Bob Pasker, Eduardo Pelegri-Lopart, Harish Prabandham, Bill Shannon, Jon S. Stevens, James Todd, Spike Washburn, and Alan Williamson爲本規範的改進和發展作出了巨大貢獻。感謝Connie Weiss, Jeff Jackson和Mala Chandra支持和推動servlet的發展提供了非凡的管理和幫助。
本規範是一項持續的、廣泛的努力的成果,包含了來自Sun及其合作伙伴的大量貢獻,尤其是如下這些公司和小組,對Servlet規範的發展作出了 巨大的貢獻:Apache Developer Community, Art Technology Group, BEA Weblogic, Clear Ink, IBM, Gefion Software, Live Software, Netscape Communications, New Atlanta Communications和Oracle。
規範的檢查和修訂過程同樣是非常有價值的。我們的合作伙伴和公衆提供了很多反饋意見來幫助我們定義和改進規範。再次,謹對所有提供反饋的人和機構致意誠摯的感謝。


1. 概述
1.1. 什麼是Servlet
Servlet是受容器管理的web組件,它能動態地生成內容。Servlet是一段小程序,被編譯成平臺無關、架構中立的的字節碼之後,可以被 Web服務器器動態地加載和運行。Servlet通過容器實現的請求-相應(request-response)方式與Web瀏覽器進行交互,這種請求- 相應模式是基於超文本傳輸協議(HTTP)的。
1.2. 什麼是Servlet容器
Servlet容器和Web服務器或者應用服務器一起提供網絡服務,能解析MIME編碼的請求,能生成MIME編碼格式的相應。容器還負責容納servlet,並對其生命週期進行管理。
Servlet容器可以內置在Web服務器中,也可以通過web服務器的擴展API作爲附加組件安裝。Servlet容器同樣可以作爲具體Web服務功能的應用服務器的內置模塊或者附加組件。
所有的Servlet容器必須支持HTTP協議,並可以支持其他基於請求-相應模式的協議,例如HTTPS。Servlet容器至少需要支持HTTP 1.0版本,並強烈建議同時支持HTTP 1.1版本。
Servlet容易可以對servlet的運行環境設置安全限制。在J2SE1.2或者J2EE 1.2環境下,這些限制條件應當使用Java2平臺所提供的授權框架來實現。例如,high end application servers 會限制某些操作,例如創建Thread對象,來保證容器的其他組件不會收到負面影響。

1.3. 一個例子
客戶端程序,如Web瀏覽器,使用HTTP請求來訪問Web服務器。請求首先被Web服務器處理,並被轉交給Servlet容器。Servlet 根據內部配置決定調用哪一個servlet,並在調用時將代表request和response的對象傳遞給它。Servlet容器可以與Web服務器運 行在同一個進程中,同一個主機的不同進程中,或者運行在不同的主機中。
Servlet通過request對象知道誰是遠程對象,哪些HTML表單參數作爲request的一部分被髮送,以及其他相關的數據。Servlet可以執行程序設定的各種邏輯,生成發還給客戶端的數據,並通過response對象將這些數據發還給客戶端。
一旦servlet完成對request的處理,servlet容器需要保證response內容被正確刷新,並將控制返還給Web服務器。

1.4. Servlet和其他技術的比較
從功能性的角度,Servlet介於CGI程序和私有服務器擴展(例如Netscape服務器API-NSAPI,Apache模塊)之間。
相對於其他的服務器擴展機制,Servlet具備如下優點:
 由於使用了不同的進程模型,其速度遠遠超過CGI腳本
 使用標準API,這些API得到大量Web服務器的支持
 擁有Java編程預言的所有有點,包括易於開發,平臺無關性
 可以使用Java平臺所提供的大量API
1.5. Servlet和J2EE的關係
Servlet API是Java 2 平臺企業版 1.2版本所需要的API。J2EE規範描述了對servlet和servlet容易的附加要求。Servlet應當被部署到容器中,而容器和servlet則都運行在J2EE環境中。
1.6. 可分佈的Servlet容器
在這個版本的規範中,增加了一個特性:將一個Web應用標記爲可分佈。這個標記允許servlet容器提供商將一個Web應用的servlet部 署在多個 Java虛擬機中,而這些虛擬機可以運行在同一臺主機上,也可以運行在不同的主機上。一個被標記爲可分佈的應用必須遵循一些限制條件,使得支持分佈式應用 的容器能實現集羣、失效轉移等特性。
高性能的環境支持可擴展性、集羣、失效轉移(J2EE兼容)。所有需要在高性能的環境下運行的Web應用,應當設計實現成爲可發佈的Web應用, 這使得應用可以最大程度地利用服務器所提供的特性。如果一個不可分佈的應用部署在這樣一個服務器上,則不能充分利用服務器提供的特性。

1.7. 自2.1版本之後的變動
自2.1版正式發佈之後,對本規範的主要變動如下:
 Web應用的概念的介紹
 web application archive files的介紹
 Response buffering(響應緩存)的介紹
 可分佈servlet的介紹
 增加通過名稱獲取RequestDispatcher的功能
 增加通過相對路徑獲取RequestDispatcher的功能
 改進國際化
 對於分佈式servlet引擎語義的一些澄清
對API做了如下變動:
 ServletConfig接口添加了getServletName方法,用於獲取在系統中表示本servlet的名稱
 ServletContext接口添加getInitParameter,getInitParameterNames這兩個方法,使初始化參數能在應用層面被設置,並被改應用的所有servlet所共享
 ServletRequest接口添加getLocale方法,幫助決定客戶端當前在哪個locale
 ServletRequest接口添加isSecure方法,用於標識request是否通過安全的方式進行傳輸,例如使用HTTPS協議
 修改了UnavailableException類的構造函數。因爲現有的構造器方面參數簽名容易被開發人員混淆。修改之後的構造器使用了更簡單的參數簽名。
 HttpServletRequest接口添加getHeaders方法,用於獲取在request中,所有用某個名稱標識的頭信息
 HttpServletRequest添加isUserInRole、getUserPrinciple兩個方法,使servlet可以使用基於抽象角色的認證
 HttpServletResponse接口添加addHeader、addIntHeader、addDateHeader三個方法,允許使用同一個名字,創建多個頭信息
 HttpSession接口添加getAttribute、getAttributeNames、setAttribute、 removeAttribute四個方法,以改進API的命名規範,相應地,getValue,、getValueNames,、setValue、 removeValue這四個方法被廢棄。
此外,還增加了大量概念的說明和澄清。



2. 術語
在本規範中,將大量使用這些術語。
2.1. 基本術語
2.1.1. 統一資源定位符
統一資源定位符(URL)是一段簡潔的字符串,用以標識在網絡上的某個資源。當通過URL訪問資源時,可能對該資源進行不同的操作處理。URL是通用資源標識(URI)的一種形式,通常使用如下格式:
<protocol>// <servername>/ <resource>
基於本規範的目的,我們主要關心基於HTTP協議的URL,其格式如下所示:
http[s]:// <servername>[:port]/ <url-path>[? <query-string>]
樣例:
http://java.sun.com/products/servlet/index.html
https://javashop.sun.com/purchase
在基於HTTP的URL中,斜槓’/’是保留字符,用於劃分URL的url-path部分分層結構的路徑。服務器負責決定分層結構的含義。url-path和某個文件系統路徑的分層結構之後,不要求存在對應關係。
2.1.2. Servlet定義
一個servlet的定義是將一個唯一的名稱與一個全限定格式的類名進行關聯,該類必須實現Servlet接口。Servlet定義中還可以設定一組初始化參數。
2.1.3. Servlet映射
一個servlet映射是在一個serlvet容器中,將一個servlet定義與一個URL路徑格式進行關聯。所有符合指定格式的請求都將被關聯的servlet處理。
2.1.4. Web應用
Web應用是一個集合,它包括servlet,JSP頁面,HTML文檔,一級其他web資源,包括圖片文件、壓縮檔案等。一個Web應用可以被打包成一個存檔文件,或者放在一個開放的目錄結構中。
所有兼容的servlet容器都必須能接受Web應用,並能將Web應用的內容部署到運行環境中。這意味着容器應當既可以通過Web應用存檔文件運行一個應用,也可以將web應用的內容移動到容器指定的特定位置,而後運行。

2.1.5. Web Application Archive(Web應用存檔)
Web應用存檔是一個單獨的文件,它包含了web應用的所有組件。這個存檔文件可使用標準的JAR工具來創建,可以對Web應用的部分或所有組件進行簽名。
Web應用存檔文件使用.war後綴名。使用這個新的後綴而不使用.jar的原因是:jar文件用於包含一組class文件,並可以通過存放在 classpath下,或通過GUI雙擊來啓動一個應用程序。而Web應用存檔文件的內容不適用於這樣的情況,故此應當使用一個新的後綴名。


2.2. 角色
基於servlet的應用的開發、部署、運行過程往往由不同的人員負責,這些人員需要進行不同的活動,並承擔不同的職責。實際情況下,一個小組可能會承擔多個角色的職責,也可能每個角色都由一個單獨的小組負責。
2.2.1. 應用開發員
應用開發人員是Web應用的生產者,其生產成果是一批servlet classes,jsp頁面,html頁面,及其相關的類庫和其他文件(如圖片文件,壓縮存檔文件等)。應用開發人員一般是應用領域專家,需要了解 servlet環境及其編程相關的知識,例如併發訪問等,並據此開發web應用。
2.2.2. 應用裝配員
應用裝配員的職責是將開發員交付的成果組裝爲一個可部署單元。其工作的輸入是開發院所提供的java classes,JSP頁面,HTML頁面,以及web應用所許需的類庫和其他文件。其工作的輸出則是Web應用存檔文件,或者是保存了Web應用的一個目錄結構
2.2.3. 部署員
部署人員的職責是將一個或多個,web應用存檔文件或者web應用目錄,部署到指定的運營環境下。運營環境往往包括指定的servlet容器和 web服務器。部署人員必須解決開發人員所聲明的所有外部依賴需求。部署人員通常使用servlet容器提供的工具來完成這些工作。
部署人員是某個運營環境的專家。例如,部署人員需要負責將開發人員定義的安全角色映射成爲運營環境下已經存在的用戶組或者帳號。
2.2.4. 系統管理員
系統管理員負責對servlet容器和web服務器進行配置和管理。系統管理員同時需要監視web應用的運行是否健康。
本規範沒有定義系統管理員的具體工作內容contracts for system management and administrator. 系統管理員通常使用容器提供商和主機製造商所提供的運行監控和管理工具來進行他們的工作。
2.2.5. Servlet容器提供商
Servlet容器提供商負責提供運行環境-servlet容器,並可能同時提供web服務器。其中一般包含一個web應用,作爲部署web應用的工具。
Servlet容器提供商主要在HTTP層面商進行編程。本規範沒有定義web服務器和servlet容器之間的接口,因此servlet容器提供商可以根據自己的需求決定兩者的邊界及其實現。
2.3. 安全術語
2.3.1. Principal(參與者)
一個principal是指可能被身份認證協議認證所的對象實體。一個principal由principal名稱來標識,並使用認證數據進行認證。Principal名稱、認證數據的內容和格式,取決於所選擇的身份認證協議。
2.3.2. Security Policy Domain(安全策略域)
Security policy domain是一個scope,其中,安全服務管理員定義了安全策略,並對其中的活動強制應用了這些安全策略。Security policy domain又經常被成爲realm(領域)。
2.3.3. Security Technology Domain(安全技術域)
Security technology domain是一個scope,其中,相同的安全機制,例如Kerboros,被用於執行安全策略。在一個security technology domain中,可以同時存在多個security policy domain.
2.3.4. 角色(Role)
在一個應用中,角色作爲一個抽象概念被開發人員定義,而部署人員則需要在一個security policy domain中將它們映射成爲具體的用用戶或者用戶組。



3. Servlet接口
Servlet接口是Servlet API的核心抽象,所有的servlet都需要實現這個接口。可以直接實現servlet接口,而更常見的方式則是通過擴展/繼承一個實現了 servlet接口的類來實現這一點。在API中提供兩個實現了servlet接口的類:GenericServlet和HttpServlet。大部分 情況下,開發人員通過繼承HttpServlet來實現自己的servlet。
3.1. 處理請求的方法(Request Handling Methods)
Servlet接口定義了一個名爲service的方法來處理客戶端的請求。每當servlet容器將request路由到一個servlet實例時,這個方法都會被調用。多個request線程可以同時調用並執行同一個service方法。
3.1.1. HTTP專用的請求處理方法(HTTP Specific Request Handling Methods)
HttpServlet抽象子類添加了幾個附加的方法,service方法會根據request自動調用這些附加的方法。這些方法是:
 doGet方法,用於處理HTTP GET請求
 doPost方法,用於處理HTTP POST請求
 doPut方法,用於處理HTTP PUT請求
 doDelete方法,用於處理HTTP DELETE請求
 doHead方法,用於處理HTTP HEAD請求
 doOptions方法,用於處理HTTP OPTIONS請求
 doTrace方法,用於處理HTTP TRACE請求
在開發基於HTTP的servlet的時候,開發人員一般只涉及doGet和doPost方法。其他的方法可視爲高級方法,爲熟悉HTTP編程的程序員準備。
Servlet開發人員可以通過實現doPut和doDelete方法來支持HTTP/1.1客戶端。HttpServlet的doHead方法 會執行 doGet方法,但是隻返回doGet方法生產的頭信息給客戶端。doOptions方法自動檢測servlet支持哪些HTTP方法,並將此信息發送給 客戶端。doTrace方法則將返回一個response,其中包含了trace 請求中的所有頭信息。
由於HTTP/1.0沒有定義PUT, DELETE, OPTIONS 和TRACE方法,因此,只支持HTTP/1.0的容器,只會使用servlet的doGet, doHead和doPost。
3.1.2. 有條件GET的支持(Conditional GET Support)
HttpServlet接口定義了getLastModified方法用於支持有條件get操作。有條件get操作是指客戶端通過HTTP GET方法請求一個資源的時候,在頭信息裏設置只有當被請求的資源在指定時間之後被修改過,才返回響應的body部分。
Servlets that implement the doGet method and that provide content that does not necessarily change from request to request should implement this method to aid in efficient utilization of network resources.
3.2. 實例數量
缺省情況下,在容器中,每個servlet定義(servlet definition)只有一個實例。
在servlet實現了SingleThreadModel接口的情況下,容器將創建多個實例,這樣容器能夠處理高負載請求,並同時保證request排隊訪問單個servlet實例。
對於一個標記爲可分佈的應用,對於容器所使用的每一個虛擬機,都將創建爲一個servlet定義的創建一個實例。如果servlet實現了SingleThreadModel方法,則對容器使用的每個虛擬機,都可以創建多個實例。
3.2.1. 關於單線程模式(SingleThreadModel)的說明
SingleThreadModel接口用於保證在同一時刻,只有一個線程,訪問一個servlet實例的service方法。需要重點說明的 是,上述保證只限於對servlet實例的訪問。對於能被多個servlet實例訪問的對象,例如HttpSession的實例對象,還是能夠被多個 servlet 實例同時訪問的,不管servlet是否實現SingleThreadModel接口。
3.3. Servlet生命週期
Servlet生命週期管理包括:如何被載入(load),如何實例化,如何初始化,如何處理客戶端情況,以及如何被銷燬。這個生命週期,在 API中,通過Servlet接口(javax.servlet.Servlet)的init,service和destroy方法得以體現。所有的 servlet都必須實現這些接口。可以直接實現servlet接口,也可以通過繼承GenericServlet或HttpServlet抽象類來實 現。
3.3.1. 載入和實例化
Servlet容器負責servlet的載入和實例化。這項工作可以在servlet引擎啓動的時候進行,也可以延遲到當容器檢測到需要servlet來處理某個請求的時候進行。
首先,servlet容器得能找到servlet對應的class文件。容器可以根據需要選擇使用java類裝載器(class loader)從本地文件系統、遠程文件系統或者網絡服務中加載這個類。
在這個類被加載之後,容器就創建該類的一個實例。
需要重點說明的是,在servlet容器中,一個servlet類可能會有多個實例。例如,多個servlet定義使用同一個類,但定義了不同的初始化參數。另外,如果servlet實現了SingleThreadModel接口,容器會創建一個實例池。
3.3.2. 初始化
在servlet對象載入和實例化之後,容器必須在servlet處理客戶端請求之前對它進行初始化。在初始化過程中,servlet可以讀取持 久化的配置數據,初始化昂貴(costly)的資源,例如jdbc數據庫連接,並執行其他一次性的操作。容器通過調用Servlet接口的init來初始 化 servlet,對於每個servlet定義,容器將創建一個唯一的、實現了ServletConfig接口的對象,並將其作爲參數傳遞給init方法。 通過這個配置信息對象,servlet可以name-value的方式獲取初始化參數。這個配置信息對象還包含了一個實現了ServletContext 接口的對象,描述了servlet的運行環境信息。關於ServletContext接口的詳細信息請看第4章。
3.3.2.1. 初始化過程出錯
在初始化過程中,servlet實例可以通過拋出UnavailableException或者ServletException異常來聲明初始 化失敗。這種情況下,容器必須釋放servlet實例,不能將其作爲一個活動的服務。由於初始化沒有成功,此時destroy方法不會被調用。
在初始化失敗的servlet實例被釋放之後,容器可以在任何時候實例化並初始化新的servlet。唯一的例外是在servlet初始化過程中拋出的UnavailableException中定義了最短失效時間,在這段時間內,不能創建新的實例。
3.3.2.2. Tool Considerations
用工具加載並分析(introspect)一個web應用的時候,它可以加載並分析(introspect)web應用的成員類,這會觸發靜態初 始化方法的執行。出於這個特性的考慮,在Servlet接口的init方法被調用之前,開發人員不能認爲servlet已經在活動在容器的運行環境內。例 如,當 servlet的靜態初始化方法被調用的時候,不應當去創建到數據庫或者EJB容器的連接
3.3.3. 請求處理
Servlet被正確初始化之後,容器就能用它來處理請求。請求被封裝在ServletRequest類型的對象中,響應信息被封裝在 ServletResponse類型的對象中,這兩個對象以參數的形式傳給Servlet接口的service方法。處理HTTP請求時,容器必須通過實 現HttpServletRequest和HttpServletResponse接口來提供請求和響應對象。
需要說明的是,servlet實例被創建成爲服務之後,可能在整個生命週期不需要響應任何請求。
3.3.3.1. 多線程問題
在處理客戶端情況過程中,容器有可能併發訪問servlet的service方法,以處理來自客戶端的併發訪問。開發人員必須注意這一點,保證servlet在併發情況下正確運行。
開發人員可以通過讓servlet實現SingleThreadModel接口來避免這種缺省行爲。通過實現這個接口可以保證同一時刻只允許一個 請求線程調用service方法。Servlet容器可以多種方式實現這個要求,可以讓訪問一個servlet的請求排隊,也可以提供servlet實例 池。如果 servlet屬於可分佈應用,容器可以在應用所分佈的每個虛擬機中保持一個servlet實例池。
如果service方法使用了synchronized修飾符(或者是HttpServlet的doGet、doPost等通過service方 法分發調用的方法),servlet容器將保證對該方法的排隊訪問,並且不能爲其創建實例池。我們強烈建議不要同步service方法和 HttpServlet的 doGet、doPost這些服務方法。
3.3.3.2. 處理請求過程中的異常
在處理請求過程中,servlet可以拋出ServletException或者UnavailableException異常。 ServletException表明處理情況過程中發生了某種錯誤,容器需要採取適當的方式來清理請求。UnavailableException異常 則表示servlet臨時性或者永久性不能響應請求。
如果servlet拋出了聲明爲永久性失效的UnavailableException,servlet容器必須移除這個servlet服務,調用其destroy方法,並釋放servlet實例。
如果是臨時性失效,在指定的最短失效期內,容器不能把請求路由到這個servlet。容器必須向被拒絕的請求返回 SERVICE_UNAVAILABLE(503)響應,並在用Retry-After後信息標明何時再次生效。容器也可以選擇不區分臨時性失效和永久性 失效,將所有UnavailableException當作永久性失效,然後移除servlet服務。
3.3.3.3. 線程安全
請求和響應對象是不保證線程安全的。這意味着它們只應當在請求處理線程範圍內被使用。請求對象和響應對象的引用不應當被傳遞給在其他線程中失效的對象,否則會產生無法預料的結果。
3.3.4. 服務終止
Servlet容器不需要在任何時刻都保持servlet處於被加載的狀態。Servlet實例的活動時間可以只有幾毫秒,可以和引擎的生命週期一樣長(幾天,幾個月,或者幾年),也可以介於兩者之間。
當servlet容器檢測到應當將一個servlet服務移除的時候(例如容器需要保留內存,或者容器被停止),必須允許servlet釋放資源,並保存持久化狀態。容器通過調用Servlet接口的destroy方法實現這一點。
在servlet容器調用destroy方法之前,必須先確保所有在servlet方法中運行的線程或者停止,或者超出服務器定義的運行限制時間。
Servlet實例的destroy方法一旦被調用,容器就不能再將請求路由到這個servlet實例。如果容器需要重新提供這個服務,必須使用新的servlet實例。
Destroy方法完成執行後,容器必須釋放servlet實例,以便進行垃圾回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章