JavaWeb基礎總結:Servlet專題


最近工作中有部分整改老接口的任務,大部分與Spring的攔截器,Tomcat相關,改到一些底層的代碼發現,對基礎J2EE的知識有些遺忘,需要頻繁查閱,索性從頭系統的整理一下Servlet和Filter的知識。

Servlet是什么

Servlet(Server Applet)是 Java Servlet 的簡稱,是使用 Java 語言編寫的運行在服務器端的程序。具有獨立於平台和協議的特性,主要功能在於交互式地瀏覽和生成數據,生成動態Web內容。通常來說,Servlet 是指所有實現了 Servlet 接口的類。Servlet的主要工作流程如下圖:

 

Servlet 的請求首先會被 HTTP 服務器(如 Apache)接收,HTTP 服務器只負責靜態 HTML 頁面的解析,而 Servlet 的請求會轉交給 Servlet 容器,Servlet 容器會根據 web.xml 文件中的映射關系,調用相應的 Servlet,Servlet 再將處理的結果返回給 Servlet 容器,並通過 HTTP 服務器將響應傳輸給客戶端。

Servlet 主要用於處理客戶端傳來的 HTTP 請求,並返回一個響應,它能夠處理的請求有 doGet() 和 doPost() 等。
Servlet 由 Servlet 容器提供,Servlet 容器是指提供了 Servlet 功能的服務器(如 Tomcat)。
Servlet 容器會將 Servlet 動態加載到服務器上,然后通過 HTTP 請求和 HTTP 應與客戶端進行交互。

Servlet的相關類及接口

Sun 公司提供了一系列的接口和類用於 Servlet 技術的開發,其中最重要的接口是 javax.servlet.Servlet。在 Servlet 接口中定義了 5 個抽象方法,具體如下表:

 Servlet接口的抽象方法
方法聲明 功能描述
void init(ServletConfig config) 容器在創建好 Servlet 對象后,就會調用此方法。該方法接收一個 ServletConfig 類型的參數,Servlet 容器通過該參數向 Servlet 傳遞初始化配置信息
ServletConfig getSendetConfig() 用於獲取 Servlet 對象的配置信息,返回 Servlet 的 ServletConfig 對象
String getServletInfo() 返回一個字符串,其中包含關於 Servlet 的信息,如作者、版本和版權等信息
voidservice (ServletRequest request,ServletResponse response) 負責響應用戶的請求,當容器接收到客戶端訪問 Servlet 對象的請求時,就會調用此方法。
容器會構造一個表示客戶端請求信息的 ServletRequest 對象和一個用於響應客戶端的 ServletResponse 對象作為參數傳遞給 service() 方法。
在 service() 方法中,可以通過 ServletRequest 對象得到客戶端的相關信息和請求信息,在對請求進行處理后,調用 ServletResponse 對象的方法設置響應信息
void destroy() 負責釋放 Servlet 對象占用的資源。當服務器關閉或者 Servlet 對象被移除時,Servlet 對象會被銷毀,容器會調用此方法

Servlet 接口中的五個方法,其中 init()、service() 和 destroy() 方法可以表現 Servlet 的生命周期,它們會在某個特定的時刻被調用。
針對 Servlet 的接口,Sun 公司提供了兩個默認的接口實現類:GenericServlet HttpServlet。其中,GenericServlet 是一個抽象類,該類為 Servlet 接口提供了部分實現,它並沒有實現 HTTP 請求處理。
HttpServletGenericServlet 的子類,它繼承了 GenericServlet 的所有方法,並且為 HTTP 請求中的 GET 和 POST 等類型提供了具體的操作方法。通常情況下,編寫的 Servlet 類都繼承自 HttpServlet,在開發中使用最多的也是 HttpServlet 對象。

HttpServlet 類的常用方法
方法聲明 功能描述
protected void doGet (HttpServletRequest req, HttpServletResponse resp) 用於處理 GET 類型的 HTTP 請求的方法
protected void doPost(HttpServletRequest req, HttpServletResponse resp) 用於處理 POST 類型的 HTTP 請求的方法

 

Servlet的生命周期

在java程序中,任何對象都有其生命周期,Servlet也不例外;

圖 中描述了 Servlet 的生命周期。Servlet容器加載之后,按照功能的不同,大致可以將 Servlet 的生命周期分為三個階段,分別是初始化階段、運行階段和銷毀階段。

初始化階段

當客戶端向 Servlet 容器發出 HTTP 請求要求訪問 Servlet 時,Servlet 容器首先會解析請求,檢查內存中是否已經有了該 Servlet 對象,如果有,則直接使用該 Servlet 對象,如果沒有,則創建 Servlet 實例對象,然后通過調用 init() 方法實現 Servlet 的初始化工作。需要注意的是,在 Servlet 的整個生命周期內,它的 init() 方法只能被調用一次。

運行階段

這是 Servlet 生命周期中最重要的階段,在這個階段中,Servlet 容器會為這個請求創建代表 HTTP 請求的 ServletRequest 對象和代表 HTTP 響應的 ServletResponse 對象,然后將它們作為參數傳遞給 Servlet 的 service() 方法。
service() 方法從 ServletRequest 對象中獲得客戶請求信息並處理該請求,通過 ServletResponse 對象生成響應結果。
在 Servlet 的整個生命周期內,對於 Servlet 的每一次訪問請求,Servlet 容器都會調用一次 Servlet 的 service() 方法,並且創建新的 ServletRequest 和 ServletResponse 對象,也就是說,service() 方法在 Servlet 的整個生命周期中會被調用多次。

銷毀階段

當服務器關閉或 Web 應用被移除出容器時,Servlet 隨着 Web 應用的關閉而銷毀。在銷毀 Servlet 之前,Servlet 容器會調用 Servlet 的 destroy() 方法,以便讓 Servlet 對象釋放它所占用的資源。在 Servlet 的整個生命周期中,destroy() 方法也只能被調用一次。
在這里,Servlet 對象一旦創建就會駐留在內存中等待客戶端的訪問,直到服務器關閉或 Web 應用被移除出容器時,Servlet 對象才會銷毀。

Servlet作用位置及主要工作流程

 Servlet 的每次請求,Web 服務器在調用 service() 方法之前,都會創建 HttpServletRequest 和 HttpServletResponse 對象。其中,HttpServletRequest 對象用於封裝 HTTP 請求消息,簡稱 request 對象。HttpServletResponse 對象用於封裝 HTTP 響應消息,簡稱 response 對象(后面會詳細介紹該部分)。詳細流程見下圖:

根據圖中可見,當瀏覽器中發生了請求事件,

    1. 會向 Web 服務器發送了一個 HTTP 請求;
    2. Web 服務器根據收到的請求,會先創建一個 HttpServletRequest 和 HttpServletResponse 對象,然后再調用相應的 Servlet 程序。
    3. 在 Servlet 程序運行時,它首先會從 HttpServletRequest 對象中讀取數據信息,然后通過 service() 方法處理請求消息,並將處理后的響應數據寫入到 HttpServletResponse 對象中。最后,Web 服務器會從 HttpServletResponse 對象中讀取到響應數據,並發送給瀏覽器。


注:在 Web 服務器運行階段,每個 Servlet 都只會創建一個實例對象,針對每次 HTTP 請求,Web 服務器都會調用所請求 Servlet 實例的 service(HttpServletRequest request,HttpServletResponse response)方法,並重新創建一個 request 對象和一個 response 對象。

 

HttpServletRequest基礎

關於Request對象

前面講了很多關於request對象的信息,下面就來詳細說一下request對象。在網絡中,我們的數據都是基於協議傳輸的,在web應用中,流通最多的就是http協議,

一個瀏覽器請求web服務器,其實本質是向web服務器發送一個http請求,而web服務器為了能更好的在web應用中使用這個http,就封裝了一個HttpServletRequest的類,並為此建立了一系列的方法及方案來創建request對象;

HttpServletRequest 接口繼承自 ServletRequest 接口,其主要作用是封裝 HTTP 請求消息。由於 HTTP 請求消息分為請求行、請求消息頭和請求消息體三部分。因此,在 HttpServletRequest 接口中定義了獲取請求行、請求頭和請求消息體的相關方法。

當訪問 Servlet 時,所有請求消息將被封裝到 HttpServletRequest 對象中,請求消息的請求行中包含請求方法、請求資源名、請求路徑等信息,為了獲取這些信息,HttpServletRequest 接口定義了一系列方法,如下表所示:


獲取請求行信息的常用方法
方法聲明 功能描述
String getMethod() 該方法用於獲取 HTTP 請求消息中的請求方式(如 GET、POST 等)
String getRequestURI() 該方法用於獲取請求行中的資源名稱部分即位於 URL 的主機和端門之后、參數部分之前的部分
String getQueryString() 該方法用於獲取請求行中的參數部分,也就是資源路徑后問號(?)以后的所有內容
String getContextPath() 該方法用於獲取請求 URL 中屬於 Web 應用程序的路徑,這個路徑以 / 開頭,表示相對於整個 Web 站點的根目錄,路徑結尾不含 /。如果請求 URL 屬於 Web 站點的根目錄,那么返回結果為空字符串("")
String getServletPath() 該方法用於獲取 Servlet 的名稱或 Servlet 所映射的路徑
String getRemoteAddr() 該方法用於獲取請求客戶端的 IP 地址,其格式類似於 192.168.0.3
String getRemoteHost() 該方法用於獲取請求客戶端的完整主機名,其格式類似於 pcl.mengma.com。需要注意的是,如果無法解析出客戶機的完整主機名,那么該方法將會返回客戶端的 IP 地址
int getRemotePort() 該方法用於獲取請求客戶端網絡連接的端口號
String getLocaIAddr() 該方法用於獲取 Web 服務器上接收當前請求網絡連接的 IP 地址
String getLocalName()
該方法用於獲取 Web 服務器上接收當前網絡連接 IP 所對應的主機名
int getLocalPort() 該方法用於獲取 Web 服務器上接收當前網絡連接的端口號
String getServerName() 該方法用於獲取當前請求所指向的主機名,即 HTTP 請求消息中 Host 頭字段所對應的主機名部分
int gctServcrPort() 該方法用於獲取當前請求所連接的服務器端口號,即 HTTP 請求消息中 Host 頭字段所對應的端口號部分
StringBuffcr getRequestURL() 該方法用於獲取客戶端發出請求時的完整 URL,包括協議、服務器名、端口號、 資源路徑等信息,但不包括后面的査詢參數部分。注意,getRequcstURL() 方法返冋的結果是 StringBuffer 類型,而不是 String 類型,這樣更便於對結果進行修改

 


  獲取請求消息頭的方法
方法聲明 功能描述
String getHeader(String name) 該方法用於獲取一個指定頭字段的值,如果請求消息中沒有包含指定的頭字段,則 getHeader() 方法返回 null;如果請求消息中包含多個指定名稱的頭字段,則 getHeader() 方法返回其中第一個頭字段的值
Enumeration getHeaders(String name)
該方法返回一個 Enumeration 集合對象,該集合對象由請求消息中出現的某個指定名稱的所有頭字段值組成。在多數情況下,一個頭字段名在請求消息中只出現一次,但有時可能會出現多次
Enumeration getHeaderNames() 該方法用於獲取一個包含所有請求頭字段的 Enumeration 對象
int getIntHeader(String name) 該方法用於獲取指定名稱的頭字段,並且將其值轉為 int 類型。需要注意的是,如果指定名稱的頭字段不存在,則返回值為 -1;如果獲取到的頭字段的值不能轉為 int 類型,則將發生 NumberFormatException 異常
long getDateHeader(String name) 該方法用於獲取指定頭字段的值,並將其按 GMT 時間格式轉換為一個代表日期/時間的長整數,該長整數是自 1970 年 1 月 1 日 0 時 0 分 0 秒算起的以毫秒為單位的時間值
String getContentType() 該方法用於獲取 Content-Type 頭字段的值,結果為 String 類型
int getContentLength() 該方法用於獲取 Content-Length 頭字段的值,結果為 int 類型
String getCharacterEncoding() 該方法用於返回請求消息的實體部分的字符集編碼,通常是從 Content-Type 頭字段中進行提取,結果為 String 類型

 

請求轉發

 當一個 Web 資源收到客戶端的請求后,如果希望服務器通知另外一個資源處理請求,可以通過RequestDispatcher 對象來解決, ServletRequest 接口中定義了一個獲取 RequestDispatcher 對象的方法。

  • RequestDispatcher getRequestDispatcher (String path)

返回封裝了某條路徑所指定資源的 RequestDispatcher 對象。其中,參數 path 必須以“/”開頭,用於表示當前 Web 應用的根目錄。需要注意的是,WEB-INF 目錄中的內容對 RequestDispatcher 對象也是可見的。因此,傳遞給 getRequestDispatcher(String path) 方法的資源可以是 WEB-INF 目錄中的文件

RequestDispatcher 接口定義了兩個相關方法。

 RequestDispatcher 接口的方法
方法聲明 功能描述
forward(ServletRequest request,ServletResponse response) 該方法用於將請求從一個 Servlet 傳遞給另一個 Web 資源。在 Servlet 中,可以對請求做一個初步處理,然后通過調用這個方法,將請求傳遞給其他資源進行響應。需要注意的是,該方法必須在響應提交給客戶端之前被調用,否則將拋出 IllegalStateException 異常
include(ServletRequest request,ServletResponse response)
該方法用於將其他的資源作為當前響應內容包含進來

 

HttpServletResponse基礎

關於Response對象

HttpServletnse 接口繼承自 ServletResponse 接口,主要用於封裝 HTTP 響應消息。由於 HTTP 響應消息分為狀態行、響應消息頭、消息體三部分。因此,在 HttpServletResponse 接口中定義了向客戶端發送響應狀態碼、響應消息頭、響應消息體的方法。

當 Servlet 向客戶端回送響應消息時,需要在響應消息中設置狀態碼。因此,HttpServletResponse 接口定義了兩個發送狀態碼的方法。

1)setStatus(int status)方法

該方法用於設置 HTTP 響應消息的狀態碼,並生成響應狀態行。由於響應狀態行中的狀態描述信息直接與狀態碼相關,而 HTTP 版本由服務器確定,因此,只要通過 setStatus(int status)方法設置了狀態碼,即可實現狀態行的發送。需要注意的是,在正常情況下,Web 服務器會默認產生一個狀態碼為 200 的狀態行。

2)sendError(int sc)方法

Servlet 向客戶端發送的響應消息中包含響應頭字段,由於 HTTP 協議的響應頭字段有很多種,因此,HttpServletResponse 接口定義了一系列設置 HTTP 響應頭字段的方法。

設置響應消息頭字段的方法
方法聲明 功能描述
void addHeader(String name,String value) 這兩個方法都是用於設置 HTTP 協議的響應頭字段。其中,參數 name 用於指定響應頭字段的名稱,參數 value 用於指定響 應頭字段的值。不同的是,addHeader() 方法可以增加同名的響應頭字段,而 setHeader() 方法則會覆蓋同名的頭字段
void setHeader (String name,String value)
void addIntHeader(String name,int value) 這兩個方法專門用於設置包含整數值的響應頭,避免了使用 addHeader() 與 setHeader() 方法時需要將 int 類型的設置值轉換為 String 類型的麻煩

void setIntHeader(String name, int value)
void setContentType(String type) 該方法用於設置 Servlet 輸出內容的 MIME 類型,對於 HTTP 協議來說,就是設置 Content-Type 響應頭字段的值。例如,如果發送到客戶端的內容是 jpeg 格式的圖像數據,就需要將響應頭字段的類型設置為 image/jpeg。需要注意的是,如果響應的內容為文本,setContentType() 方法還可以設置字符編碼,如 text/html;charset = UTF-8
void setLocale (Locale loc) 該方法用於設置響應消息的本地化信息。對 HTTP 來說,就是設置 Content-Language 響應頭字段和 Content-Type 頭字段中的字符集編碼部分。需要注意的是,如果 HTTP 消息沒有設置 Content-Type 頭字段,則 setLocale() 方法設置的字符集編碼不會出現在 HTTP 消息的響應頭中,如果調用 setCharacterEncoding() 或 setContentType() 方法指定了響應內 容的字符集編碼,則 setLocale() 方法將不再具有指定字符集編碼的功能
void setCharacterEncoding(String charset) 該方法用於設置輸出內容使用的字符編碼,對 HTTP 協議來說,就是設置 Content-Type 頭字段中的字符集編碼部分。如果沒有設置 Content-Type 頭字段,則 setCharacterEncoding 方法設 置的字符集編碼不會出現在 HTTP 消息的響應頭中。setCharacterEncoding() 方法比 setContentType() 和 setLocale() 方法的優先權高,它的設置結果將覆蓋 setContentType() 和 setLocale() 方法所設置的字符碼表

 於在 HTTP 響應消息中,大量的數據都是通過響應消息體傳遞的,因此,ServletResponse 遵循以 I/O 流傳遞大量數據的設計理念。在發送響應消息體時,定義了兩個與輸出流相關的方法。

1)getOutputStream() 方法

該方法所獲取的字節輸出流對象為 ServletOutputStream 類型。由於 ServletOutputStream是OutputStream 的子類,它可以直接輸出字節數組中的二進制數據。因此,要想輸出二進制格式的響應正文,就需要使用 getOutputStream() 方法。

2)getWriter() 方法

該方法所獲取的字符輸出流對象為 PrintWriter 類型。由於 PrintWriter 類型的對象可以直接輸出字符文本內容,因此,要想輸出內容全部為字符文本的網頁文檔,則需要使用 getWriter() 方法。

注意:雖然 response 對象的 getOutputStream() 和 getWriter() 方法都可以發送響應消息體,但是,它們之間互相排斥,不可同時使用,否則會發生 IllegalStateException 異常

請求重定向

在某些情況下,針對客戶端的請求,一個 Servlet 類可能無法完成全部工作。這時,可以使用請求重定向完成這一工作。
請求重定向指 Web 服務器接收到客戶端的請求后,可能由於某些條件的限制,不能訪問當前請求 URL 所指向的 Web 資源,而是指定了一個新的資源路徑,讓客戶端重新發送請求

為了實現請求重定向,HttpServletResponse 接口定義了一個 sendRedirect() 方法,該方法用於生成 302 響應碼和 Location 響應頭,從而通知客戶端重新訪問 Location 響應頭中指定的 URL

sendRedirect() 方法的工作原理如圖

當客戶端訪問 Servlet1 時,由於在 Servlet1 中調用了 sendRedirect() 方法將請求重定向到 Servlet2,因此,瀏覽器收到 Servlet1 的響應消息后,立刻向 Servlet2 發送請求,Servlet2 對請求處理完畢后,再將響應消息回送給客戶端瀏覽器並顯示。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM