1. 程式人生 > >【Java.Web】Servlet —— Servlet的類的體系結構

【Java.Web】Servlet —— Servlet的類的體系結構

Servlet API

以TOMCAT為例,<CATALINA_HOME>/lib/servlet-api.jar檔案為Servlet API的類庫檔案。

Servlet API主要由兩個Java包組成: javax.servlet和javax.servlet.http


  • 在javax.servlet包中定義了Servlet介面及相關的通用介面和類;
  • 在javax.servlet.http包中主要定義了與HTTP協議相關的HttpServlet類,HttpServletRequest介面和HttpServletResponse介面;

Servlet介面

在Servlet介面中定義了5個方法,其中3個方法都是由Servlet容器來呼叫的,容器會在Servlet的生命週期的不同階段呼叫特定的方法:


  • init(ServletConfig) —— 負責初始化Servlet物件,容器在建立好Servlet物件後,就會呼叫該方法;
  • service(ServletRquest req, ServletResponse res) —— 負責相應客戶的請求,為客戶提供相應服務。當容器接受到客戶端要求訪問特定Servlet物件的請求時,就會呼叫該Servlet物件的service()方法;
  • destroy() —— 負責釋放Servlet物件佔用的資源。當Servlet物件結束宣告週期時,容器會呼叫此方法;

GenericServlet抽象類


GenericServlet抽象類為Servlet介面提供了通用實現,它與任何網路應用層協議無關。

GenericServlet除了實現了Servlet介面,還實現了ServletConfig介面和Serializable介面:

public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable

GenericServlet類實現了Servlet介面中的init(ServletConfig config)初始化方法。GenericServlet類有一個ServletConfig型別的private成員變數,當Servlet容器呼叫GenericServlet的init(ServletConfig)方法時,該方法使得私有變數引用由容器傳入的ServletConfig物件。GenericServlet類還定義了一個不帶引數的init()方法,init(ServletConfig)方法會呼叫此方法。因此在子類中重寫init時,最好重寫init()方法,若重寫Init(ServletConfig)方法,還需要先呼叫父類的init(ServletConfig)方法(super.init(config))。

GenericServlet類沒有實現Servlet介面中的service()方法。service()方法是GenericServlet類中唯一的抽象方法,GenericServlet類的具體子類必須實現該方法。

GenericServlet類實現了Servlet介面的destroy()方法,但實際什麼也沒做。

GenericServlet類實現了ServletConfig介面的所有方法。

HttpServlet抽象類

HttpServlet類是GenericServlet類的子類。HttpServlet類為Serlvet介面提供了與HTTP協議相關的通用實現,也就是說,HttpServlet物件適合執行在與客戶端採用HTTP協議通訊的Servlet容器或者Web容器中。

在我們自己開發的Java Web應用中,自定義的Servlet類一般都擴充套件自HttpServlet類

HttpServlet類實現了Servlet介面中的service(ServletRequest , ServletResponse)方法,而該方法實際呼叫的是它的過載方法HttpServlet.service(HttpServletRequest, HttpServletResponse);

在上面的過載service()方法中,首先呼叫HttpServletRequest型別的引數的getMethod()方法,獲得客戶端的請求方法,然後根據該請求方式來呼叫匹配的服務方法;如果為GET方式,則呼叫doGet()方法,如果為POST方式,則呼叫doPost()方法。

HttpServlet類為所有的請求方式,提供了預設的實現doGet(),doPost(),doPut(),doDelete()方法;這些方法的預設實現都會向客戶端返回一個錯誤。

對於HttpServlet類的具體子類,一般會針對客戶端的特定請求方法,覆蓋HttpServlet類中的相應的doXXX方法。如果客戶端按照GET或POST方式請求訪問HttpsServlet,並且這兩種方法下,HttpServlet提供相同的服務,那麼可以只實現doGet()方法,並且讓doPost()方法呼叫doGet()方法。

ServletRequest介面

ServletRequest表示來自客戶端的請求;當Servlet容器接收到客戶端要求訪問特定Servlet的請求時,容器先解析客戶端的原始請求資料,把它包裝成一個ServletRequest物件。

ServletRequest介面提供了一系列用於讀取客戶端的請求資料的方法,例如:

  • getContentLength() —— 返回請求正文的長度,如果請求正文的長度未知,則返回-1;
  • getContentType() —— 獲得請求正文的MIME型別,如果請求正文的型別為止,則返回null;
  • getInputStream() —— 返回用於讀取請求正文的輸入流;
  • getLocalAddr() —— 返回服務端的IP地址;
  • getLocalName() —— 返回服務端的主機名;
  • getLocalPort() —— 返回服務端的埠號;
  • getParameters() —— 根據給定的請求引數名,返回來自客戶請求中的匹配的請求引數值;
  • getProtocal() —— 返回客戶端與伺服器端通訊所用的協議名稱及版本號;
  • getReader() —— 返回用於讀取字串形式的請求正文的BufferReader物件;
  • getRemoteAddr() —— 返回客戶端的IP地址
  • getRemoteHost() —— 返回客戶端的主機名
  • getRemotePort() —— 返回客戶端的埠號

HttpServletRequest介面

HttpServletRequest介面是ServletRequest介面的子介面。

HttpServletRequest介面提供了用於讀取HTTP請求中的相關資訊的方法:

  • getContextPath() —— 返回客戶端請求方法的Web應用的URL入口,例如,如果客戶端訪問的URL為http://localhost:8080/helloapp/info,那麼該方法返回“/helloapp”;
  • getCookies() —— 返回HTTP請求中的所有Cookie;
  • getHeader(String name) —— 返回HTTP請求頭部的特定項;
  • getHeaderName() —— 返回一個Enumeration物件,它包含了HTTP請求頭部的所有專案名;
  • getMethod() —— 返回HTTP請求方式;
  • getRequestURL() —— 返回HTTP請求的頭部的第一行中的URL;
  • getQueryString() —— 返回HTTP請求中的查詢字串,即URL中的“?”後面的內容;

ServletResponse介面

Servlet通過ServletResponse物件來生成響應結果。

ServletResponse介面定義了一系列與生成響應結果相關的方法,例如:

  • setCharacterEncoding() —— 設定相應正文的字元編碼。響應正文的預設字元編碼為ISO-8859-1;
  • setContentLength() —— 設定響應正文的長度;
  • setContentType() —— 設定響應正文的MIME型別;
  • getCharacterEncoding() —— 獲得響應正文的字元編碼
  • getContentType() —— 獲得響應正文的MIME型別
  • setBufferSize() —— 設定用於存放響應正文資料的緩衝區的大小
  • getBufferSize() —— 獲得用於存放響應正文資料的緩衝區的大小;
  • reset() —— 清空緩衝區內的正文資料,並且清空響應狀態程式碼及響應頭
  • resetBuffer() —— 僅僅清空緩衝區的正文資料,不清空響應狀態程式碼及響應頭;
  • flushBuffer() —— 強制性地把緩衝區內的響應正文資料傳送到客戶端;
  • isCommitted() —— 返回一個boolean型別的值,如果為true,表示緩衝區內的資料已經提交給客戶,即資料已經發送到客戶端;
  • getOutputStream() —— 返回一個ServletOutputStream物件,Servlet用它來輸出二進位制的正文資料
  • getWriter() —— 返回一個PrinterWriter物件,Servlet用它來輸出字串形式的正文資料

ServletResponse中響應正文的預設MIME型別是text/plain,即純文字型別,而HttpServletResponse中響應正文的預設MIME型別為text/html,即HTML文件型別。

為了提高輸出資料的效率,ServletOutputStream和PrintWriter首先把資料寫到緩衝區內。當緩衝區內的資料被提交給客戶後,ServletResponse的isComitted方法返回true。在以下幾種情況下,緩衝區內的資料會被提交給客戶,即資料被髮送到客戶端:

  • 當緩衝區內的資料已滿時,ServletOutPutStream或PrintWriter會自動把緩衝區內的資料傳送給客戶端,並且清空緩衝區;
  • Servlet呼叫ServletResponse物件的flushBuffer方法;
  • Servlet呼叫ServletOutputStream或PrintWriter物件的flush方法或close方法;

為了確保SerlvetOutputStream或PrintWriter輸出的所有資料都會被提交給客戶,比較安全的做法是在所有資料都輸出完畢後,呼叫ServletOutputStream或PrintWriter的close()方法(Tomcat中,會自動關閉)。

如果要設定響應正文的MIME型別和字元編碼,必須先呼叫ServletResponse物件的setContentType()和setCharacterEncoding()方法,然後再呼叫ServletResponse的getOutputStream()或getWriter()方法,提交緩衝區內的正文資料;只有滿足這樣的操作順序,所做的設定才能生效。

HttpServletResponse介面

HttpServletResponse介面提供了與HTTP協議相關的一些方法,Servlet可通過這些方法來設定HTTP響應頭或向客戶端寫Cookie。

  • addHeader() —— 向HTTP響應頭中加入一項內容
  • sendError() —— 向客戶端傳送一個代表特定錯誤的HTTP響應狀態程式碼
  • setHeader() —— 設定HTTP響應頭中的一項內容,如果在響應頭中已經存在這項內容,則原來的設定被覆蓋
  • setStatus() —— 設定HTTP響應的狀態程式碼
  • addCookie() —— 向HTTP響應中加入一個Cookie

在HttpServletResponse介面中定義了一些代表HTTP響應狀態程式碼的靜態常量

ServletConfig介面

當Servlet容器初始化一個Servlet物件時,會為這個Servlet物件建立一個ServletConfig物件

在Servlet物件中包含了Servlet的初始化引數資訊

ServletConfig介面中定義了以下方法:

  • getInitParameter(String name) —— 返回匹配的初始化引數值
  • getInitParameterNames() —— 返回一個Enumeration物件,裡面包含了所有的初始化引數名
  • getServletContext() —— 返回一個ServletContext物件
  • getServletName() —— 返回Servlet的名字,即web.xml檔案中相應<servlet>元素的<servlet-name>子元素的值;如果沒有為servlet配置<servlet-name>子元素,則返回Servlet類的名字

HttpServlet類繼承了GenericServlet類,而GenericServlet類實現了ServletConfig介面,因此HttpServlet或GenericServlet類及子類中都可以直接呼叫ServletConfig介面中的方法。

ServletContext介面

ServletContext是Servlet與Servlet容器之間直接通訊的介面。

Servlet容器在啟動一個Web應用時,會為它建立一個ServletContext物件。每個Web應用都有唯一的ServletContext物件,可以把ServletContext物件形象地理解為Web應用的總管家,同一個Web應用中的所有Servlet物件都共享一個ServletContext,Servlet物件可以通過其訪問容器中的各種資源

ServletContext介面提供的方法可以分為以下幾種型別:

  • 用於在web應用範圍記憶體取共享資料的方法:
    • setAttribute(String name, Object object) —— 把一個Java物件與一個屬性名繫結,並存入到ServletContext中;
    • getAttribute() —— 返回指定數姓名的屬性值
    • getAttributeNames() —— 返回一個Enumeration物件,包含所有存放在ServletContext中的屬性名
    • removeAttributes() —— 從ServletContext中刪除匹配的屬性
  • 訪問當前Web應用的資源:
    • getContextPath() —— 返回當前Web應用的URL入口
    • getInitParameter() —— 返回Web應用範圍內的匹配的初始化引數值。在web.xml中,直接在<web-app>根元素下定義的<context-param>元素表示應用範圍內的初始化引數
    • getServletContextName() —— 返回Web應用的名字,即web.xml檔案中<display-name>元素的值
    • getRequestDispatcher() —— 返回一個用於向其他WEB元件轉發請求的RequestDispatcher物件
  • 訪問Servlet容器中的其他WEB應用:
  • 訪問Servlet容器的相關資訊:
  • 訪問伺服器端的檔案系統資源:
    • getRealPath() —— 根據引數指定的虛擬路徑,返回檔案系統中的一個真實的路徑
    • getResources() —— 返回一個對映到引數指定的路徑的URL
    • getResourceAsStream() —— 返回一個用於讀取引數指定的檔案的輸入流
    • getMimeType() —— 返回引數指定的檔案MIME型別
  • 輸出日誌:
    • log(String msg) —— 向Servlet的日誌檔案中寫日誌
    • log(String message, Throwable throwable) —— 向Servlet的日誌檔案中寫入錯誤日誌,以及異常的堆疊資訊

Servlet相關類的關係


與Servlet主動關聯的是三個類,分別是ServletConfig,ServletRequest和ServletResponse。這三個類都是通過容器傳遞給Servlet的;其中,ServletConfig是在Servlet初始化時傳給Servlet的,後兩個是在請求到達時呼叫Servlet傳遞過來的。

對於Request和Response,以TOMCAT為例,tomcat接到請求首先將會建立org.apache.coyote.Request和org.apache.coyote.Response,這兩個類是Tomcat內部使用的描述一次請求和相應的資訊類,它們是一個輕量級的類,作用就是在伺服器接收到請求後,經過簡單解析將這個請求快速分配給後續執行緒去處理。接下來當交給一個使用者執行緒去處理這個請求時又建立org.apache.catalina.connector.Request和org.apache.catalina.connector.Response物件。這兩個物件一直貫穿整個Servlet容器直到要傳給Servlet,傳給Servlet的是Request和Response的Facade類。

Request和Response的轉變過程:


Servlet如何工作

當用戶從瀏覽器向伺服器發起的一個請求通常會包含如下資訊:

http://hostname:port/contextpath/servletpath

hostname和port用來與伺服器建立TCP連線,後面的URL用來選擇在伺服器中哪個子容器服務使用者的請求。

在Tomcat7中,這種對映工作由專門的一個類完成:org.apache.tomcat.util.http.mapper,這個類儲存了tomcat的container容器中的所有子容器的資訊。org.apache.catalina.connector.Request類在進入Container容器之前,Mapper將會根據這次請求的hostname和contextpath將host和context容器設定到Request的mappingData屬性中,所以當Request進入container容器之前,對於它要訪問哪個子容器就已經確定了。

Servlet的實際使用

  • 我們自己定義的servlet通常去繼承HttpServlet或GenericServlet類。
  • 採用MVC框架的實現中,其基本原理是將所有的請求都對映到一個Servlet,然後去實現servie方法,這個方法也就是MVC框架的入口。