此次分享講一下 Java Web 相關的基礎知識,主要就是 servlet
部分的知識。涉及到的知識點比較的多,若是同窗們來不及看,能夠先收藏起來,有空的時候再慢慢看哦!下面咱們步入正題。html
協議
就是一套約定好的規則,只要咱們遵循其中的規則就能很好的進行溝通與協做。HTTP
協議也同樣,HTTP 協議嚴格規定了 HTTP 請求和 HTTP 響應的數據格式,只要 HTTP 服務器與客戶程序之間的交換數據都遵照 HTTP 協議,雙方就能看得懂對方發送的數據,從而順利交流。HTTP
協議位於 應用層
,創建在 TCP/IP
協議的基礎上。HTTP 協議使用可靠的 TCP
鏈接,默認端口 80
端口。HTTP 響應也由3部分構成:java
如下是一些常見的狀態碼:web
200
:表示服務器已經成功地處理了客戶端發出的請求。400
:錯誤的請求,客戶發送的HTTP請求不正確。404
:文件不存在。405
:服務器不支持客戶的請求方式。500
:服務器內部錯誤。HTTP 請求及響應的正文部分能夠是任意格式的數據,如何保證接收方能看得懂發送方發送的 正文數據
呢?HTTP協議採用 MIME 協議來規範正文的數據格式。設計模式
遵循 MIME 協議的數據類型統稱爲 MIME 類型。在 HTTP 請求頭和 HTTP 響應頭中都有一個 Content-Type
項,用來指定 請求正文部分
或 響應正文
部分的 MIME 類型。下標列出了常見的 MIME 類型與文件擴展名之間的對應關係。數組
文件擴展名 | MIME類型 |
---|---|
未知的數據類型或不可識別的擴展名 | content/unknown |
.bin、.exe、.o、.a、.z | application/octet-stream |
application/pdf | |
.zip | application/zip |
.tar | application/x-tar |
.gif | image/gif |
.jpg、.jpeg | image/jpeg |
.htm ,html | text/html |
.text .c .h .txt .java | text/plain |
.mpg .mpeg | video/mpeg |
.xml | application/xml |
什麼是Web服務器呢?Web服務器
是由專門的服務器開發商建立 ,用來發布和運行Web應用的。Web 服務器如何能動態執行由第三方建立的Web應用中的程序代碼呢?因此咱們須要一個 中介方
制定 Web應用
與 Web 服務器
進行協做的標準接口,Servlet
就是其中最主要的一個接口。中介方規定:瀏覽器
Servlet 規範把可以發佈和運行 JavaWeb
應用的 Web服務器
稱爲 Servlet 容器
,它的最主要的特徵是動態執行 JavaWeb 實現類中的程序代碼。常見的 Servlet 容器有 Tomcat、 Jetty、WebLogic、WebSphere、JBoss 等。安全
Tomca 做爲 Servlet 容器的基本功能 以下圖所示,Tomcat
做爲運行 Servlet 的容器,其基本功能是 負責接收和解析來自客戶的請求
,同時把客戶的請求 傳送給相應的 Servlet
,並把 Servlet 的響應結果
返回給客戶。bash
Servlet 容器響應客戶請求訪問特定 Servlet 的流程以下:服務器
爲了讓 Servlet 容器能順利地找到 JavaWeb 應用中的各個組件,Servlet 規範規定,JavaWeb 應用必須採用固定的目錄結構。Servlet 規範還規定,JavaWeb 應用的配置信息存放在 WEB-INF/web.xml 文件中,Servlet 容器從該文件中讀取配置信息。cookie
假定開發一個名爲 helloapp 的 JavaWeb 應用,首先,應該建立這個Web應用的目錄結構,以下表所示:
目錄 | 描述 |
---|---|
/helloapp | Web應用的根目錄。 |
/helloapp/WEB-INF | 存放Web應用的配置文件web.xml |
/helloapp/WEB-INF/classes | 存放各類.class文件,Servlet 類的 .class 文件也放於此目錄下 |
/helloapp/WEB-INF/lib | 存放Web應用所需的各類JAR文件。例如,JDBC驅動程序的JAR文件。 |
能夠看出 Servlet 容器並不關心你的源代碼放在哪裏,它只關心 .class 文件,由於加載類只須要用到 .class 文件。在 WEB-INF 目錄的 classes及 lib 子目錄下,均可以存放 Java 類文件。在運行時,Servlet 容器的類加載器先加載 classes 目錄下的類,再加載 lib 目錄下的 JAR 文件(Java類庫的打包文件)中的類。所以,若是兩個目錄下存在同名的類,classes 目錄下的類具備優先權。
大佬終於出場了,你們掌聲歡迎!本節主要介紹的就是 Servlet 中常常須要使用到的 "神器「。
請求對象
(ServletRequest 和 HttpServletRequest):Servlet 從該對象中獲取來自客戶端的請求信息。響應對象
(ServletResponse 和 HttpServletResponse):Servlet 經過該對象來生成響應結果。Servlet配置對象
(ServletConfig):當容器初始化一個 Servlet 對象時,會向 Servlet 提供一個 ServletConfig 對象,Servlet 經過該對象來獲取初始化參數信息及 ServletContext 對象。Servlet上下文對象
(ServletContext):Servlet 經過該對象來訪問容器爲當前 Web 應用提供的各類資源。Servlet API 主要由兩個 Java 包組成:javax.servlet
和 javax.servlet.http
。
javax.servlet
:包中定義了 Servlet 接口及相關的通用接口和類;javax.servlet.http
包中主要定義了與 HTTP 協議相關的 HTTPServlet 類、HTTPServletRequest 接口和 HTTPServletResponse 接口。Servlet API 的核心是 javax.servlet.Servlet
接口,全部的 Servlet 類都必須實現這一接口。在 Servlet 接口中定義了5個方法,其中3個方法都由 Servlet 容器來調用,容器會在 Servlet的生命週期的不一樣階段調用特定的方法。
init(ServletConfig config)
:負責初始化 Servlet 對象。容器在建立好 Servlet 對象後,就會調用該方法。service(ServletRequest req, ServletResponse res)
:負責響應客戶的請求,爲客戶提供相應服務。當容器接收到客戶端要求訪問特定 Servlet 對象的請求時,就會調用該 Servlet 對象的 service() 方法。destroy()
:負責釋放Servlet對象佔用的資源。當 Servlet 對象結束生命週期時,容器會調用此方法。Servlet 接口還定義瞭如下兩個返回 Servlet 的相關信息的方法。JavaWeb 應用中的程序代碼能夠訪問 Servlet 的這兩個方法,從而得到 Servlet 的配置信息及其餘相關信息。
getServletConfig()
:返回一個 ServletConfig 對象,在該對象中包含了 Servlet 的初始化參數信息。getServletInfo()
:返回一個字符串,在該字符串中包含了Servlet的建立者、版本和版權等信息。在 Servlet API 中,java.servlet.GenericServlet
抽象類實現了 Servlet 接口,而 javax.servlet.http.HttpServlet
抽象類是 GenericServlet
類的子類。當用戶開發本身的 Servlet 類時,能夠選擇擴展 GenericServlet
類或者 HTTPServlet
類。
GenericServlet
抽象類爲 Servlet 接口提供了通用實現,它與任何 網絡應用層協議無關
。GenericServlet 類除了實現 Servlet 接口,還實現了 ServletConfig 接口和 Serializable 接口。
從GenericServlet 類的源代碼能夠看出,GenericServlet 類實現了 Servlet 接口中的 init(ServletConfig config)
初始化方法。GenericServlet
類有一個 ServletConfig 類型的私有實例變量config
,當 Sevlet容器
調用 GenericServlet
的 init(ServletConfig config)
方法時,該方法使得私有實例變量 config
引用由 容器傳入
的 ServletConfig 對象,即便得 GenericServlet 對象與一個 ServletConfig 對象關聯。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
複製代碼
GenericServlet類還自定義了一個不帶參數的 init() 方法,init(ServletConfig config)
方法會調用此方法。對於GenericServlet類的子類,若是但願覆蓋父類的初始化行爲,有如下兩種辦法:
public void init(){
// 子類具體的初始化行爲
}
複製代碼
init(ServletConfig config)
方法。若是但願當前 Servlet 對象與 ServletConfig 對象關聯,應該如今該方法中調用 super.init(config) 方法:public void init(ServletConfig config){
// 調用父類的init(config)方法
super.init(config);
// 子類具體的初始化行爲
}
複製代碼
GenericServlet 類沒有實現 Servlet 接口中的 service() 方法。service() 方法是 GenericServlet 類中惟一的抽象方法,GenericServlet 類的具體子類必須實現該方法,從而爲特定的客戶請求提供具體的服務。
此外,GenericServlet 類實現了 ServletConfig 接口中的全部方法。所以,GenericServlet 類的子類能夠直接調用在 ServletConfig 接口中定義的 getServletContext()、getInitParameter() 和 getInitParameterNames() 等方法。
GenericServlet 類實現了 Servlet 接口和 ServletConfig 接口。GenericServlet 類的主要身份是 Servlet,此外,它還運用 裝飾設計模式
,爲本身附加了 ServletConfig 裝飾身份。在具體實現中,GenericServlet 類包裝了一個 ServletConfig 接口的實例,經過該實例來實現 ServletConfig 接口中的方法。
HttpServlet 類是 GenericServlet 類的子類。HttpServlet 類爲 Servlet 接口提供了與 HTTP協議
相關的通用實現,也就是說,HttpServlet 對象適合運行在客戶端採用 HTTP 協議通訊的 Servlet 容器或者 Web 服務器中。在開發 JavaWeb 應用時,自定義的 Servlet 類通常都擴展 HttpServlet 類。
HTTP 協議把客戶請求分爲 GET、POST、PUT 和 DELETE 等多種方式。HttpServlet 類針對每一種請求方式都提供了相應的服務方法,如doGet()、doPost()、doPut和doDelete()等方法。
從 HttpServlet 的源碼能夠看出,HttpServlet 類實現了 Servlet 接口中的 service(ServletRequest req,ServletResponse res) 方法,該方法實際上調用的是它的重載方法:
service(HttpServletRequest req, HttpServletResponse resp)
在以上重載 service() 方法中,首先調用 HttpServletRequest 類型的 req 參數的 getMethod()
方法,從而得到客戶端的請求方式,而後依據該請求方式來調用匹配的服務方法。若是爲 GET 方法,則調用 doGet() 方法;若是爲 POST 方式,則調用 doPost() 方法,依次類推。
在 Servlet 接口的 service(ServletRequest req, ServletResponse res)
方法中有一個 ServletRequest 類型的參數。ServletRequest 類表示來自客戶端的請求。當 Servlet 容器接收到客戶端要求訪問特定 Servlet 的請求時,容器先解析客戶端的原始請求數據,把它包裝成一個ServletRequest 對象
。當容器調用 Servlet 對象的 service() 方法時,就能夠把 ServletRequest 對象做爲參數傳給 service() 方法。
ServletRequest 接口提供了一系列用於讀取客戶端的請求數據的方法:
getContentLength()
:返回請求正文的長度。若是請求正文的長度未知,則返回-1。
getContentType()
:得到請求正文的MIME類型。若是請求正文的類型未知,則返回null。
getInputStream()
:返回用於讀取請求正文的輸入流。
getParameter(String name)
:根據給定的請求參數名,返回來自客戶端請求中的匹配的請求參數值。
getReader()
:返回用於讀取字符串形式的請求正文的BufferedReader對象。等等
此外,在 ServletRequest 接口中還定義了一組用於在請求範圍內存取共享數據的方法:
getAttribute(String name,Object object)
:在請求範圍內保存一個屬性,參數 name 表示屬性名,參數 object 表示屬性值。
getAttribute(String name)
:根據 name 參數給定的屬性名,返回請求範圍內的匹配的屬性值。
removeAttribute(String name)
:從請求範圍內刪除一個屬性。
HttpServletRequest 接口是 ServletRequest 接口的子接口。HttpServlet 類的重載 service() 方法及 doGet() 和doPost() 等方法都有一個 HttpServletRequest 類型的參數。
HttpServletRequest 接口提供了用於讀取HTTP請求中的相關信息的方法:
getContextPath()
:返回客戶端所請求訪問的Web應用的URL入口。例如,客戶端訪問的URL爲 http://localhost:8080/helloapp/info
,那麼該方法返回 」/helloapp"
getCookies()
:返回HTTP請求中的全部 Cookie。
getHeader(String name)
:返回 HTTP 請求頭部的特定項。
getHeaderNames()
:返回一個 Enumeration 對象,它包含了 HTTP 請求頭部的全部項目名。
getMethod()
:返回 HTTP 請求方式。
getRequestURI()
:返回HTTP請求的頭部的第一行中的 URI。
getQueryString()
:返回HTTP請求中的查詢字符串,即URL中的 ?
後面的內容。
在 Servlet 接口的 service(ServletRequest req, ServletResponse res)
方法中有一個 ServletResponse 類型的參數。Servlet 經過 ServletResponse 對象來生成響應結果。當 Servlet 容器接收到客戶端要求訪問特定 Servlet 的請求時容器會建立一個 ServletResponse 對象,並把它做爲參數傳給 Servlet 的 service() 方法。
在 ServletResponse 接口中定義了一系列與生成響應結果相關的方法。
getOutputStream()
:返回一個 ServletOutputStream 對象,Servlet 用它來輸出二進制的正文數據。
getWriter()
:返回一個PrintWriter對象,Servlet 用它來輸出字符串像是的正文數據。
ServletResponse
中響應正文的默認 MIME 類型爲 text/plain
,即純文本類型。而 HttpServletResponse
中響應正文的默認 MIME 類型爲 text/html
,即HTML文檔類型。
爲了提升輸出數據的效率,ServletOutputStream 和 PrintWriter 先把數據寫到緩衝區內。當緩衝區內的數據被提交給客戶後,ServletResponse 的 isCommitted() 方法返回 true。在如下幾種狀況下,緩衝區內的數據會被提交給客戶,即數據被髮送到客戶端:
當緩衝區內的數據 已滿
時,ServletOutputStream 或 PrintWriter 會自動把緩衝區內的數據發送給客戶端,而且清空緩衝區。
Servlet 調用 ServletResponse
對象的 flushBuffer()
方法。
Servlet 調用 ServletOutputStream
或 PrintWriter
對象的 flush()
方法或 close()
方法。
爲了確保 ServletOutputStream 或 PrintWriter 輸出的全部數據都會被提交給客戶,比較安全的作法是在全部數據都輸出完畢後,調用 ServletOutputStream 或 PrintWriter 的 close()
方法。
在 Tomcat 的實現中,若是 Servlet 的 service() 方法沒有調用 ServletOutputStream 或 PrintWriter 的 close() 方法,那麼 Tomcat
在調用完 Servlet 的 service() 方法後,會 關閉
ServletOutputStream 或 PrintWriter,從而確保 Servlet 輸出的全部數據被提交給客戶。
值得注意的是,若是要設置響應正文的MIME類型和字符編碼,必須 先調用
ServletResponse 對象的 setContentType()
和 setCharacterEncoding()
方法,而後 再調用
ServletResponse 的 getOutputStream() 或 getWriter() 方法,或者提交緩衝區內的正文數據。只有知足這樣的操做順序,所作的設置才能生效。
HttpServletResponse 接口是 ServletResponse 的子接口,HttpServlet 類的重載 service() 方法及 doGet 和 doPost() 等方法都有一個 HttpServletResponse 類型的參數。
HttpServletResponse接口提供了與HTTP協議相關的一些方法,Servlet可經過這些方法來設置HTTP響應頭或向客戶端寫Cookie。
addHeader(String name,String value)
:向HTTP響應頭中加入一項內容。
setHeader(String name,String msg)
:設置HTTP響應頭中的一項內容。若是在響應頭中已經存在這項內容,那麼原先所作的設置將被覆蓋。
sendError(int sc)
:向客戶端發送一個表明特定錯誤的HTTP響應狀態代碼。
sendError(int sc,String msg)
:向客戶端發送一個表明特定錯誤的HTTP響應狀態代碼,而且發送具體的錯誤消息。
setStatus(int sc)
:設置HTTP響應的狀態代碼。
addCookie(Cookie cookie)
:向HTTP響應中加入一個Cookie。
如下3種方式都能設置 HTTP 響應正文的 MIME 類型及字符編碼:
// 方式一
response.setContentType(「text/html;charset=utf-8」);
// 方式二
response.setContentType(「text/html」);
response.setCharacterEncoding(「utf-8」);
// 方式三
response.setHeader(「Content-type」,」text/html;charset=utf-8」);
複製代碼
Servlet接口的 init(ServletConfig congfig)
方法有一個 ServletConfig 類型的參數。當 Servlet 容器初始化一個 Servlet 對象時,會爲這個 Servlet 對象建立一個 ServletConfig 對象。在 ServletConfig
對象中包含了 Servlet 的 初始化參數信息
,此外,ServletConfig 對象還與當前Web應用的 ServletContext 對象關聯。
ServletConfig 接口中定義瞭如下方法。
getInitParameter(String name):根據給定的初始化參數名,返回匹配的初始化參數值。
getInitParameterNames():返回一個Enumeration對象,裏面包含了全部的初始化參數名。
getServletContext():返回一個ServletContext對象。
每一個初始化參數包括一對參數名和參數值。在web.xml文件中配置一個Servlet時,能夠經過 元素來設置初始化參數。 元素的 子元素設定參數名, 子元素設定參數值。
HttpServlet 類繼承 GenericServle t類,而 GenericServlet 實現了 ServletConfig 接口,所以在 HttpServlet 或GenericServlet 類及子類中均可以直接調用 ServletConfig 接口中的方法。
ServletContext
是 Servlet
與 Servlet 容器
之間 直接通訊
的接口。Servlet容器在啓動一個Web應用時,會爲它建立一個ServletContext對象。每一個Web應用都有 惟一
的ServletContext對象,能夠把 ServletContext
對象形象地理解爲Web應用的總管家,同一個Web應用中的全部Servlet對象都共享一個總管家
(敲黑板了,全部Servlet對象共享一個 ServletContext 對象) ,Servlet對象們可經過這個總管家來訪問容器中的各類資源。
Servlet 容器在 啓動一個Web應用時
,會爲它建立 惟一
的 ServletContext 對象。當 Servlet 容器終止一個Web應用時,就會銷燬它的ServletContext對象。因而可知,ServletContext 對象與Web應用具備 相同的生命週期
。
下面展現 ServletContext 接口常用的幾個方法:
用於 Web應用範圍內
存取 共享數據
的方法。
setAttribute(String name,Object object)
:把一個Java對象與一個屬性名綁定,並把它存入到ServletContext中。
getAttribute(String name)
:根據參數給定的屬性名,返回一個Object類型的對象,它表示ServletContext中與屬性名匹配的屬性值。
getAttributeNames()
:返回一個Enumeration對象,該對象包含了全部存放在ServletContext中的屬性名。
removeAttribute(String name)
:根據參數指定的屬性名,從ServletContext中刪除匹配的屬性。
訪問當前Web應用的資源。
getContextPath()
:返回當前Web應用的URL入口。
getInitParameter(String name)
:根據給定的參數名,返回Web應用範圍內的匹配的初始化參數值。在web.xml文件中,直接在 根元素下定義的 元素表示應用範圍內的初始化參數。
getInitParameterNames()
:返回一個Enumeration對象,它包含了Web應用範圍內全部初始化參數名。
getServletContextName()
:返回Web應用的名字,即web.xml文件中<display-name>元素的值。
getRequestDispatcher(String path)
:返回一個用於向其餘Web組件轉發請求的RequestDispatcher對象。
在 ServletConfig 接口中定義了 getServletContext() 方法。HttpServlet 類繼承 GenericServlet類,而GenericServlet 類實現了 ServletConfig 接口,所以在 HttpServlet 類或 GenericServlet 類及子類中均可以直接調用 getServletContext() 方法,從而獲得當前Web應用的 ServletContext 對象。
JavaWeb 應用的生命週期是由 Servlet 容器來控制的。概括起來,JavaWeb 應用的生命週期包括 3 個階段。
啓動階段:加載Web應用的有關數據,建立ServletContext對象 ,對Filter和一些Servlet進行初始化。
運行時階段:爲客戶端服務。
終止階段:釋放Web應用所佔用的各類資源。
Servlet 容器在啓動 JavaWeb 應用時,會完成如下操做:
把 web.xml
文件中的數據加載到內存中。
爲 JavaWeb 應用建立一個 ServletContext
對象。
對全部的 Filter
進行初始化。
對那些須要在 Web 應用 啓動時就被初始化
的 Servlet
進行初始化。
這是 JavaWeb 應用最主要的生命階段。在這個階段,它的全部 Servlet 都處於待命狀態,隨時能夠響應客戶端的特定請求,提供相應的服務。加入客戶端請求的 Servlet 還不存在,Servlet 容器會先初始化Servlet ,而後再調用它的 service() 服務。
Servlet 容器在終止 JavaWeb 應用時,會完成如下操做:
銷燬 JavaWeb 應用中全部處於運行時狀態的 Servlet。
銷燬 JavaWeb 應用中全部處於運行時狀態的 Filter。
銷燬全部與 JavaWeb 應用相關的對象,若是 ServletContext 對象等,而且釋放 Web 應用所佔用的相關資源。
JavaWeb 應用的生命週期由 Servlet 容器來控制,而 Servlet 做爲 JavaWeb 應用的最核心的組件,其生命週期也由 Servlet 容器來控制。Servlet 的生命週期能夠分爲3個階段:初始化階段、運行時階段和銷燬階段。在javax.servlet.servlet接口中定義了3個方法:init()、service() 和 destroy(),它們將分別在 Servlet 的不一樣階段被 Servlet 容器調用。
Servlet 的初始化階段包括 4 個步驟:
Servlet 容器加載 Servlet 類,把它的 .class 文件中的數據讀入到內存中。
Servlet 容器建立 ServletConfig 對象。 ServletConfig
對象包含了 特定
Servlet 的 初始化配置信息
,如 Servlet 的初始化參數。此外, Servlet 容器還會使得 ServletConfig 對象與當前Web應用的 ServletContext
對象 關聯
。
Servlet 容器建立 Servlet 對象。
Servlet 容器調用 Servlet 對象的 init(ServletConfig config) 方法。
以上初始化步驟建立了 Servlet 對象和 ServletConfig 對象,而且 Servlet 對象與 ServletConfig 對象關聯,而 ServletConfig 對象又與當前 Web 應用的 ServletContext 對象關聯。當 Servlet 容器初始化完 Servlet 後,Servlet 對象只要經過 getServletContext() 方法就能獲得當前Web應用的 ServletContext 對象。
在下列狀況之一,Servlet會進入初始化階段。
當前Web應用處於運行時階段,特定 Servlet 被客戶端首次請求訪問。多數 Servlet 都會在這種狀況下被 Servlet 容器初始化階段。
若是在 web.xml 文件中爲一個 Servlet 設置了 元素,那麼當 Servlet 容器啓動 Servlet 所屬的Web應用時,就會初始化這個Servlet。假如 Servlet1 和 Servlet2 的 的值分別爲1和2,所以Servlet容器啓動當前Web應用時,Servlet1第一個初始化,Servlet2被第二個初始化。而沒有配置 元素的Servlet,當Servlet容器啓動當前Web應用時將不會被初始化,只有當客戶端首次請求訪問該Servlet時,它纔會被初始化。
當Web應用被從新啓動時,Web應用中的全部Servlet都會在特定的時刻被從新初始化。
這是 Servlet 的聲明週期中的最重要階段。在這個階段,Servlet 能夠隨時響應客戶端的請求。當 Servlet 容器接收到要求訪問特定 Servlet 的客戶請求時,Servlet 容器會建立針對於這個請求的 ServletRequest 對象和 ServletResponse 對象,而後調用相應 Servlet 對象的 service() 方法。service() 方法從 ServletRequest 對象中得到客戶請求信息並處理該請求,在經過 ServletResponse 對象生成響應結果。
當 Servlet 容器把 Servlet 生成的 響應結果發送
給了客戶,Servlet 容器就會 銷燬
ServletRequest 對象和 ServletResponse 對象。
當Web應用被終止時
,Servlet 容器會先調用Web應用中 全部Servlet
對象的 destroy()
方法,而後 再銷燬
這些Servlet對象。在destroy()方法的實現中,能夠釋放Servlet所佔用的資源。
此外,容器還會銷燬與對象關聯的 ServletConfig
對象。
在 Servlet API 中有一個 ServletContextListener
接口,它可以監聽 ServletContext 對象的生命週期,實際上就是監聽 Web應用 的生命週期。
當Servlet容器啓動或終止Web應用時,它可以監聽 ServletContextEvent 事件,該事件由ServletContextListener來處理。在ServletContextListener接口中定義了處理ServletContextEvent事件的兩個方法。
contextInitialized(ServletContextEvent sec)
:當Servlet容器 啓動
Web應用時調用該方法。在調用完該方法 以後
,容器再對 Filter初始化
,而且對那些在Web應用啓動時就須要被初始化的 Servlet 進行初始化。
contextDestroyed(ServletContextEvent sec)
:當Servlet容器 終止
Web應用調用該方法。在調用該方法 以前
,容器會先 銷燬
全部的 Servlet
和 Filter
過濾器。
能夠看出,在Web應用的生命週期中,ServletContext 對象最先被建立,最晚被銷燬。
用戶自定義的 ServletContextListener 監聽器只有先向 Servlet 容器註冊,Servlet 容器在啓動或終止 Web 應用時,纔會調用該監聽器的相關方法。在 web.xml 爲文件中, 元素用於向容器註冊監聽器
<listener>
<listener-class>LinstnerClass</listener-class>
</listener>
複製代碼
Cookie的英文原意是「點心」,它是在客戶端訪問Web服務器時,服務器
在 客戶端硬盤上存放的信息
。當客戶端 首次
訪問服務器時,服務器如今客戶端存放包含該客戶的相關信息的Cookie,之後客戶端每次請求訪問服務器時,都會在 HTTP 請求數據中包含 Cookie,服務器解析 HTTP 請求中的 Cookie,就能由此得到關於用戶的相關信息。
Cookie的運行機制是由HTTP協議規定的,多數 Web 服務器和瀏覽器都支持 Cookie。Web服務器爲了支持Cookie,須要具有如下功能。
在HTTP響應結果中添加Cookie數據。
解析HTTP請求中的Cookie數據。
瀏覽器爲了支持Cookie,須要具有如下功能。
解析HTTP響應結果中的Cookie數據。
把Cookie數據保存到本地硬盤。讀取本地硬盤上的Cookie數據,把它添加到HTTP請求中。
Tomcat 做爲Web服務器,對Cookie提供了良好的支持。那麼,運行在Tomcat中的Servlet該如何訪問Cookie呢?Java Servlet API爲Servlet訪問Cookie提供了簡單易用的接口,Cookie 用 javax.servlet.http.Cookie
類來表示,每一個 Cookie 對象包含一個 Cookie 名字和 Cookie 值。
下面代碼建立了一個 Cookie 對象,而後調用 HttpServletResponse 的 addCookie()
方法,把 Cookie 添加到 HTTP 響應結果中:
Cookie theCookie = new Cookie(「username」,」Tom」);
response.addCookie(theCookie);
複製代碼
若是Servlet想讀取來自客戶端的Cookie,那麼能夠經過如下方式從HTTP請求中取得全部的Cookie:
Cookie[] cookies = request.getCookies();
複製代碼
對於每一個 Cookie 對象,可調用 getName()
方法來得到 Cookie 的名字,調用 getValue()
方法來得到 Cookie 的值。
當 Servlet 向客戶端寫 Cookie 時,還能夠經過 Cookie 類的 setMaxAge(int expiry)
方法來設置 Cookie 的有效期。參數 expiry 以秒爲單位,它具備如下含義:
若是 expiry 大於零,就指示瀏覽器在客戶端硬盤上保存 Cookie 的時間爲 expiry 秒,有效期內,其餘瀏覽器也能訪問這個 Cookie。
若是 expiry 等於零,就指示瀏覽器刪除當前 Cookie。
若是 expiry 小於零,就指示瀏覽器不要把 Cookie 保存到客戶端硬盤。Cookie 僅僅存在於當前瀏覽器進程中,當瀏覽器進程關閉,Cookie 也就消失。
Cookie默認的有效期爲-1。
服務器對客戶端進行讀寫 Cookie 操做,會給客戶端帶來安全隱患。服務器可能會向客戶端發送包含惡意代碼的Cookie 數據,此外,服務器可能會依據客戶端的 Cookie 來竊取用戶的保密信息。所以出於安全起見,多數瀏覽器能夠設置是否啓用 Cookie。
當客戶端訪問 Web 應用時,在許多狀況下,Web 服務器必須可以跟蹤客戶的狀態。
Web服務器跟蹤用戶的狀態一般有4種方法:
在HTML表單中加入隱藏字段,它包含用於跟蹤用戶狀態的數據。
重寫URL,使它包含用於跟蹤客戶狀態的數據。
用Cookie來傳送用於跟蹤客戶狀態的數據。
使用會話(Session)機制 。
HTTP 是無狀態的協議。在Web開發領域,會話機制是用於跟蹤客戶狀態的廣泛解決方案。會話指的是在一段時間內,單個客戶與Web應用的一連串相關的交互過程。在一個會話中,客戶可能屢次請求訪問Web應用的同一個網頁,也有可能請求訪問同一個Web應用中的多個網頁。
Servlet 規範制定了基於Java的會話的具體運做機制。在Servlet API中定義了表明會話的 javax.servlet.http.HttpSession
接口,Servlet容器必須實現這一接口。當一個會話開始時,Servlet容器將建立一個 HttpSession 對象,在該對象中能夠存放表示客戶狀態的信息。Servlet容器爲每一個 HttpSession 對象分配一個惟一標誌符,符號位 Session ID
。
key:客戶端 Cookie 只負責存 Session ID,而 Session 對象是存儲在服務器上的。
下面以一個名爲 bookstore 應用爲例,介紹會話的運做流程:
一個瀏覽器進程第一次請求訪問bookstore應用中的任意一個支持會話的網頁,Servlet 容器試圖尋找 HTTP 請求中表示 Session ID 的 Cookie ,因爲還不存在這樣的 Cookie ,所以就認爲一個新的會話開始了,因而建立一個 HttpSession 對象,爲它分配惟一的 Session ID ,而後把 Session ID 做爲 Cookie 添加到 HTTP 響應結果中。當瀏覽器接收到 HTTP 響應結果後,會把其中表示 Session ID 的 Cookie 保存在客戶端。
瀏覽器進程繼續請求訪問 bookstore 應用中的任意一個支持會話的網頁,在本次 HTTP 請求中會包含表示Session ID 的 Cookie。Servlet 容器試圖尋找 HTTP 請求中表示 Session ID 的 Cookie,因爲能獲得這樣的 Cookie 。所以認爲本次請求已經處於一個會話中了,Servlet容器再也不建立新的 HttpSession 對象,而是從 Cookie 中獲取 Session ID ,而後根據 Session ID 找到內存中對應的 HttpSession 對象。
瀏覽器進程重複步驟二,直到當前會話被銷燬,HttpSession對象就會結束生命週期。
表示Session ID的Cookie的有效期爲-1,這意味着該Cookie也就消失,本次會話也會結束。在兩個瀏覽器進程中顯式的Session ID的值不同,由於兩個瀏覽器進程分別對應不一樣的會話,而每一個會話都有惟一的Session ID。
會話範圍是指瀏覽器端與一個Web應用進行一次會話的過程。在具體實現上,會話範圍與 HttpSession 對象的生命週期對應。所以,Web組件只要共享同一個 HttpSession 對象,也就能共享會話範圍內的共享數據。
HttpSession接口中的方法描述以下表,Web應用中的JSP或Servlet組件可經過這些方法來訪問會話。
方法 | 描述 |
---|---|
getId() | 返回 Session ID。 |
invalidate() | 銷燬當前的會話,Servlet容器會釋放HttpSession對象佔用的資源。 |
setAttribute(String name, Object value) | 將一對name/value屬性保存在HttpSession對象中。 |
getAttribute(String name) | 根據name參數返回保存在HttpSession對象中的屬性值。 |
getAttributeNames() | 以數組的方式返回HttpSession對象中全部屬性名。 |
removeAttribute(String name) | 從HttpSession對象中刪除name參數的指定屬性。 |
isNew() | 判斷是不是新建立的會話 |
setMaxInactiveInterval(int interval) | 設定一個會話能夠處於不活動狀態的最長時間,以秒爲單位。若是超過這個時間,Servlet容器會自動銷燬會話。若是把參數interval設置爲負數,表示不限制會話處於不活動狀態的時間,即會話永遠不會過時。Tomcat爲會話設定的默認的保持不活動狀態的最長時間爲1800秒。 |
getMaxInactiveInterval() | 讀取當前會話能夠處於不活動狀態的最長時間。 |
在如下狀況下,會開始一個新的會話,即Servlet容器會建立一個新的 HttpSession 對象:
一個瀏覽器進程第一訪問Web應用中的支持會話的任意一個網頁。
當瀏覽器進程與Web應用的一次會話已經被銷燬後,瀏覽器進程再次訪問Web應用中的支持會話的任意一個網頁。
在如下狀況下,會話被銷燬,即Servlet容器使HttpSession對象結束生命週期,而且存放在會話範圍內的共享數據也都被銷燬:
瀏覽器進程終止。
服務器端執行 HttpSession 對象的 invalidate() 方法。
會話過時。
當Tomcat中的Web應用被終止時,它的會話不會被銷燬,而是被Tomcat持久化到永久性存儲設備中,當Web應用重啓後,Tomcat會從新加載這些會話。
當一個會話開始後,若是瀏覽器進程忽然關閉,Servlet容器端沒法當即知道瀏覽器進程已經被關閉,所以Servlet容器端的HttpSession對象不會當即結束生命週期。不過,當瀏覽器進程關閉後,這個會話就進入不活動狀態,等到超過了 setMaxInactiveInterval(int interval)
方法設置的時間,會話就會由於過時而被 Servlet 容器銷燬。
key:是否是屬於同一個 session,看 sessionId 是否相同就能夠了。而 sessionId 又保存在瀏覽器的 cookie 中,顯然不一樣瀏覽器保存的cookie是不同的。
能夠經過 HttpServletRequest 對象來得到 HttpSession 對象。
在 HttpServletRequest 接口中提供了兩個與會話有關的方法:
getSession()
:是的當前 HttpServlet 支持會話。假如會話已經存在,就返回相應的 HttpSession 對象,不然就建立一個新會話,並返回新建的 HttpSession 對象。該方法等價於調用 HttpServletRequest 的 getSession(true)
方法。
getSession(boolean create)
:若是參數 create 爲 true
,等價於調用 HttpServletRequest 的 getSession() 方法;若是參數 create 爲 false ,那麼假如會話已經存在,就返回響應的HttpSession對象,不然就返回 null。
key:一個常見的誤解是覺得s ession 在有客戶端訪問時就被建立,然而事實是直到某 server 端程序調用HttpServletRequest.getSession(true)這樣的語句時才被建立。
cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。
cookie不是很安全,別人能夠分析存放在本地的COOKIE並進行COOKIE欺騙,考慮到安全應當使用session。
session會在必定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能,考慮到減輕服務器性能方面,應當使用COOKIE。
單個cookie保存的數據不能超過4K,不少瀏覽器都限制一個站點最多保存20個cookie。
轉發能夠將請求轉發給同一Web應用的組件。
// 把請求轉發給OutputServlet
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher("/OutputServlet");// ok
// RequestDispatcher dispatcher = context.getRequestDispatcher("outputServlet");//worng
// RequestDispatcher dispatcher = req.getRequestDispatcher("outputServlet");//ok
PrintWriter out = res.getWriter();
out.println("Output from CheckServlet before forwading request.");
System.out.println("Output from CheckServlet before forwading request.");
// throw IllegalArgumentException:Cannot forward after response has been committed
//out.close();
dispatcher.forward(req, res);
out.println("Output from CheckServlet after forwading request.");
System.out.println("Output from CheckServlet after forwading request.");
複製代碼
public class OutputServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// 讀取CheckServlet存放在請求範圍內的消息
String message = (String) req.getAttribute("msg");
PrintWriter out = res.getWriter();
out.println(message);
out.close();
}
}
複製代碼
控制檯打印結果:
Output from CheckServlet before forwading request.
Output from CheckServlet after forwading request.
以上 dispatcher.forward(request,response)
方法的處理流程以下:
清空用於存放響應正文數據的緩衝區。
若是目標組件爲 Servlet 或 JSP,就調用它們的 service() 方法,把該方法產生的響應結果發送到客戶端;若是目標組件爲文件系統中的靜態 HTML 文檔,就讀取文檔中的數據並把它發送到客戶端。
從 dispatcher.forward(request,response)
方法的處理流程能夠看出,請求轉發具備如下特色:
因爲 forward() 方法先清空用於存放響應正文數據的緩衝區,所以 Servlet 源組件生成的響應結果不會被髮送到客戶端,只有目標組件生成的響應結果纔會被髮送到客戶端。
若是源組件在進行請求轉發 以前
,已經提交了響應結果(例如調用ServletResponse的flushBuffer()方法,或者調用與ServletResponse關聯的輸出流的close()方法),那麼 forward() 方法會拋出 lllegalStateException
。爲了不該異常,不該該在源組件中提交響應結果。
在 Servlet 源組件中調用 dispatcher.forward(request,response)
方法以後的代碼也會被執行。同理調用out.close() 以後的代碼也會執行。只是 out.close() 以後的代碼不會再經過 response 返回到客戶端。
下面代碼把 header.html 的內容,GreetServlet 生成的響應內容以及 foot.html 的內容都包含到本身的響應結果中。也就是說,下面的Servlet返回給客戶的HTML文檔是由自身、header.htm、GreetServlet,以及foot.htm共同產生的。
ServletContext context = getServletContext();
RequestDispatcher headDispatcher = context.getRequestDispatcher("/header.htm");
RequestDispatcher greetDispatcher = context.getRequestDispatcher("/greet");
RequestDispatcher footDispatcher = context.getRequestDispatcher("/footer.htm");
headDispatcher.include(request, response);
greetDispatcher.include(request, response);
footDispatcher.include(request, response);
複製代碼
RequestDispatcher對象的include()方法的處理流程以下。
若是目標組件爲Servlet或JSP,就調用它們的相應的service()方法,把該方法產生的響應正文添加到源組件的響應結果中;若是目標組件爲HTML文檔,就直接把文檔的內容添加到源組件的響應結果中。
返回到源組件的服務方法中,繼續執行後續代碼塊。
包含與請求轉發相比,前者有如下特色:
源組件與被包含的目標組件的輸出數據都會被添加到響應結果中。
在目標組件中對響應狀態代碼或者響應頭所作的修改都會被忽略。
當源組件和目標組件之間爲請求轉發關係或者包含關係時,對於每一次客戶請求,它們都共享同一個ServletRequest對象及ServletResponse對象,所以源組件和目標組件能共享請求範圍內的共享數據。
HTTP 協議規定了一種重定向機制,重定向的運做流程以下:
用戶在瀏覽器輸入特定URL,請求訪問服務器端的某個組件。
服務器端的組件返回一個狀態碼爲302的響應結果,響應結果的含義爲:讓瀏覽器端再請求訪問另外一個Web組件。在響應結果中提供了另外一個Web組件的URL。另外一個Web組件有可能在同一個Web服務器上,也可能不在同一個Web服務器上。
當瀏覽器端接收到這種響應結果後,再當即自動請求訪問另外一個Web組件。
瀏覽器端接收到來自另外一個Web組件的響應結果。
在Java Servlet API中,HttpServletResponse接口的 sendRedirect(String location)
方法用於重定向。
重定向以後,導航欄的 URL 會變成目標組件的 URL 地址。response.sendRedirect(String location)
方法具備如下特色:
Servlet源組件生成的響應結果不會被髮送到客戶端。response.sendRedirect(String location)
方法一概返回狀態碼爲302的響應結果,瀏覽器接收到這種響應結果後,再當即自動請求訪問重定向的目標Web組件,客戶端最後接收到的是目標Web組件的響應結果。
若是源組件在進行重定向以前,已經提交了響應結果,那麼 sendRedirect() 方法會拋出異常。爲了不該異常,不該該在源組件中提交響應結果。
在Servlet源組件中調用 response.sendRedirect(String location)
方法以後的代碼也會被執行。
源組件和目標組件不共享同一個 ServletRequest 對象,所以不共享請求範圍內的共享數據。
對於 response.sendRedirect(String location)
方法中的參數location,若是以」/「開頭,表示相對於當前服務器根路徑的URL,若是以」http://「開頭,表示一個完整的URL。
目標組件沒必要是同一個服務器上的同一個Web應用中的組件,它能夠是Internet上的任意一個有效的網頁。
sendRedirect() 方法是在 HttpServletResponse 接口中定義的,而在 ServletResponse 接口中沒有 sendRedirect() 方法,由於重定向機制是由 HTTP 協議規定的。
forward 方法只能轉發給同一個web站點的資源,而 sendRedirect 方法還能夠定位到同一個web站點的其餘應用。
forward 轉發後,瀏覽器 URL 地址不變,sendRedirect 重定向後,瀏覽器url地址變爲目的 URL 地址。
forward 轉發的過程,在一個 servlet 中調用了同一個web應用的另外一個 servlet 的 service() 方法,因此仍是屬於一次請求響應。sendRedirect,瀏覽器先向目的Servlet發送一次請求,Servlet 看到 sendRedirect 將目的 URL 返回到瀏覽器,瀏覽器再去請求目的 URL ,目的 URL 再返回response到瀏覽器。瀏覽器和服務器兩次請求響應。
forward 方法的調用者與被調用者之間共享 Request 和 Response。 sendRedirect 方法因爲兩次瀏覽器服務器請求,因此有兩個 Request 和 Response。
各個 Web 組件中的 相同操做
能夠放到一個 過濾器
中來完成,這樣就能減小重複編碼。過濾器可以對一部分客戶請求先進行預處理操做,而後再把請求轉發給響應的 Web 組件,等到 Web 組件生產了響應結果後,過濾器還能對響應結果進行檢查和修改,而後再把修改後的響應結果發送給客戶。
過濾器負責過濾的 Web 組件能夠是 Servlet、JSP 或 HTML 文件,過濾器的過濾過程以下圖所示。
全部自定義的過濾器都必須實現 javax.servlet.Filter
接口,這個接口含如下 3 個必須實現的方法。
init(FilterConfig filterConfig)
:這是過濾器的初始化方法。在 Web 應用啓動時
, Servlet 容器先建立包含了過濾器配置信息的 FilterConfig
對象,而後建立 Filter
對象,接着調用 Filter 對象的 init(FilterConfig filterConfig)
方法,在這個方法中可經過 config
參數來讀取 web.xml
文件中爲過濾器配置的初始化參數。
doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
:這個方法完成實際的過濾操做。當客戶請求訪問的 URL 與爲過濾器映射的 URL 匹配時, Servlet 容器將先調用過濾器的 doFilter()
方法。FilterChain
參數用於訪問 後續過濾器
或者 Web組件
。
destroy()
:Servlet 容器在銷燬過濾器對象前調用該方法,在這個方法中能夠釋放過濾器佔用的資源。
doFilter()是整個過濾器中最爲關鍵的一個方法,servlet 容器調用該方法完成實際的過濾操做
該方法接收兩個參數,分別爲 request、response 參數,因此咱們能夠對 request 進行一些預處理以後再繼續傳遞下去,也能夠對返回的 response 結果進行一些額外的操做,再返回到客戶端(前提是response沒有被 close)。
在 Filter.doFilter()
方法中不能直接調用 Servlet 的 service
方法,而是調用 FilterChain.doFilter
方法來激活目標 Servlet 的 service
方法,FilterChain 對象時經過 Filter.doFilter
方法的參數傳遞進來的。
在一個 Web 應用程序中能夠註冊多個
Filter
程序,若是有多個 Filter
程序均可以對某個 Servlet
程序的訪問過程進行攔截,當針對該 Servlet
的訪問請求到達時,Web 容器將把這多個 Filter
程序組合成一個 Filter
鏈(也叫過濾器鏈)。
Filter 鏈中的各個 Filter 的攔截順序與它們在 web.xml 文件中的映射順序一致,上一個 Filter.doFilter 方法中調用 FilterChain.doFilter 方法將激活下一個 Filter的doFilter 方法,最後一個 Filter.doFilter 方法中調用的 FilterChain.doFilter 方法將激活目標 Servlet的service 方法。
只要 Filter 鏈中任意一個 Filter 沒有調用 FilterChain.doFilter 方法,則目標 Servlet 的 service 方法都不會被執行。
下面經過一個簡單的例子演示基本的過濾器使用。
定義過濾器
該過濾器主要的功能就是在請求以前打印一下初始化參數,請求以後拼接一下返回結果:
public class LogFilter implements Filter {
private String ip;
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("執行LogFilter初始化方法");
// 獲取Filter初始化參數
this.ip = filterConfig.getInitParameter("ip");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("正在執行 LogFilter.doFilter()方法,LogFilter初始化參數ip=" + ip);
System.out.println("調用LogFilter中的chain.doFilter()以前");
chain.doFilter(request, response);
System.out.println("調用LogFilter chain.doFilter()以後");
PrintWriter out = response.getWriter();
// 拼接返回結果
out.println("This msg is from LogFilter");
out.flush();
}
public void destroy() {
System.out.println("正在執行 LogFilter 的銷燬方法");
}
}
複製代碼
TestFilter servlet
public class TestFilter extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("正在執行 TestFilter 的 doGet() 方法");
PrintWriter out = response.getWriter();
out.println("This msg is from HelloServlet doGet()");
// 若是執行 close 操做,後續過濾器中對 response 的操做 將沒法返回到客戶端
// out.close();
out.flush();
}
}
複製代碼
在 web.xml 中配置過濾器
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>controller.LogFilter</filter-class>
<init-param>
<param-name>ip</param-name>
<param-value>127.0.0.1</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/testFilter</url-pattern>
</filter-mapping>
複製代碼
控制檯打印結果以下:
正在執行 LogFilter.doFilter()方法,LogFilter初始化參數ip=127.0.0.1
調用LogFilter中的chain.doFilter()以前
正在執行 TestFilter 的 doGet() 方法
調用LogFilter chain.doFilter()以後
http響應正文以下:
This msg is from HelloServlet doGet()
This msg is from LogFilter
多個過濾器能夠串聯起來協同工做,Servlet 容器將根據它們在 web.xml 中定義的前後順序,一次調用它們的doFilter() 方法。假定有兩個過濾器串聯起來,它們的 doFilter() 方法均採用如下結構:
Code1; //表示chain.doFilter()前的代碼
chain.doFilter();
Code2; //表示chain.doFilter()後的代碼
複製代碼
假定這兩個過濾器都會爲同一個 Servlet 預處理客戶請求。當客戶請求訪問這個 Servlet 時,這兩個過濾器及 Servlet 的工做流程以下圖所示。
因爲篇幅的問題,這裏就再也不展現串聯過濾器的用法了,感興趣的同窗能夠自行寫個小demo嘗試一下。只須要本身新建一個過濾器,而後早web.xml文件中添加一些相關配置便可。
在 Java 語言中,局部變量和實例變量有着不一樣的做用域,它們的區別以下:局部變量在一個方法中定義,每一個線程都擁有本身的局部變量。實例變量在類中定義。類的每個實例都擁有本身的實例變量,若是一個實例結束生命週期,那麼屬於它的實例變量也就結束生命週期。若是有多個線程同時執行一個實例的方法,而這個方法會訪問一個實例變量,那麼這些線程訪問的是同一個實例變量。
以爲對您有幫助,歡迎評論轉發點贊哦!
參考資料: