1. 程式人生 > >JSP/Servlet 工作原理

JSP/Servlet 工作原理

Servlet

Servlet 沒有 main 方法,不能夠獨立的執行,它的執行需要容器的支援,Tomcat 是最常用的 JSP/Servlet 容器。

Servlet 執行在 Servlet 容器中,並由容器管理從建立到銷燬的整個過程。

對於使用者到達Servlet的請求,Servlet容器會建立特定於這個請求的ServletRequest物件和ServletResponse物件,然後呼叫Servlet的service方法。service方法從ServletRequest物件獲得客戶請求資訊,處理該請求,並通過ServletResponse物件向客戶返回響應資訊。

Servlet 的生命週期

首先引入維基百科的內容:

1、當servlet被部署在應用伺服器中(應用伺服器中用於管理Java元件的部分被抽象成為容器)以後,由容器控制servlet的生命週期。

2、除非特殊制定,否則在容器啟動的時候,servlet是不會被載入的,servlet只會在第一次請求的時候被載入和例項化。

3、servlet一旦被載入,一般不會從容器中刪除,直至應用伺服器關閉或重新啟動。但當容器做記憶體回收動作時,servlet有可能被刪除。也正是因為這個原因,第一次訪問servlet所用的時間要大大多於以後訪問所用的時間。

注:

1、servlet在伺服器中的執行:1.載入 ->2.初始化 - > 3.呼叫 - 4.銷燬

2、生命週期:servlet的生命週期在【2.初始化】後開始其生命週期,在【4.銷燬】後結束其生命週期

3、一般情況下,一個servlet例項對應一種請求,此servlet例項可以處理多個使用者請求。

-----------------------------

經過google發現,很多人都在爭論Servlet是否屬於單例模式。給出的這兩個網頁內容可以看出:

1、Servlet不是單例的.不過一般來說一個servlet只會有一個例項。

2、由於Servlet/JSP預設是以多執行緒模式執行的,所以,在編寫程式碼時需要非常細緻地考慮多執行緒的安全性問題。這裡參考部落格servlet單例項多執行緒模式

百度百科

Servlet的例項過程一般如下:

當伺服器接收一個客戶端請求時,需要做一下四件事情。

(1) 載入和例項化

如果Servlet容器還沒例項化一個Servlet物件,此時容器裝載和例項化一個 Servlet。創建出該 Servlet 類的一個例項。如果已經存在一個Servlet物件,此時不再建立新例項。

(2) 初始化
在產生 Servlet 例項後,容器負責呼叫該 Servlet 例項的 init() 方法,在處理使用者請求之前,來做一些額外的初始化工作。
(3) 處理請求
當 Servlet 容器接收到一個 Servlet 請求時,便執行與之對應的 Servlet 例項的 service() 方法,service() 方法根據使用者的請求呼叫相對應的doGet或doPost 方法來處理使用者請求。然後再進入對應的方法中呼叫邏輯層的方法,實現對客戶的響應。
(4) 銷燬
當 Servlet 容器決定將一個 Servlet 從伺服器中移除時 ( 如 Servlet 檔案被更新 ),便呼叫該 Servlet 例項的 destroy() 方法,在銷燬該 Servlet 例項之前,來做一些其他的工作。

其中,(1)(2)(4) 在 Servlet 的整個生命週期中只會被執行一次。

注意:

0、如果在web.xml中對servlet配置了load-on-startup,那麼就是在容器載入Servlet初始化。 

  1. <servlet>  
  2.             <servlet-name>AAA</servlet-name>  
  3.             <servlet-class>servlet.AAA</servlet-class>  
  4.             <load-on-startup>1</load-on-startup>  
  5. </servlet>  

1、一般的Servlet啟動後只有一個例項(對同一Servlet只配置一個mapping),如果一個Servlet有多個mapping,那麼會有多個Servlet例項。

2、在web.xml檔案中,某些Servlet只有<serlvet>元素,沒有<servlet-mapping>元素,這樣我們無法通過url的方式訪問這些Servlet,這種Servlet通常會在<servlet>元素中配置一個<load-on-startup>子元素,讓容器在啟動的時候自動載入這些Servlet並呼叫init()方法,完成一些全域性性的初始化工作。也就是說,如果要通過某個網址載入某些Servlet時,需要寫<servlet-mapping>元素。如果Servlet是在容器載入時執行時,則不需要寫<servlet-mapping>元素.

3、當Servlet只有一個例項,當有多個客戶端訪問時,會多次呼叫已經例項化好的service方法處理請求。有時容器根據客戶機請求生成Servlet物件例項或生成多個Servlet物件例項並將其加入SERVLET例項池中,來處理請求,參考討論

Servlet 的工作原理

結合右邊給出的流程圖:

當客戶端瀏覽器向伺服器請求一個 Servlet 時,伺服器收到該請求後,首先到容器中檢索與請求匹配的 Servlet 例項是否已經存在。

--若不存在,則 Servlet 容器負責載入並例項化出該類 Servlet的一個例項物件,接著容器框架負責呼叫該例項的 init() 方法來對例項做一些初始化工作,然後Servlet 容器執行該例項的 service() 方法。

--若 Servlet 例項已經存在,則容器框架直接呼叫該例項的 service() 方法。
service() 方法在執行時,自動派遣執行與使用者請求相對應的 doXX() 方法來響應使用者發起的請求。

通常,每個 Servlet 類在容器中只存在一個例項,每當請求到來時,則分配一條執行緒來處理該請求。

在處理請求時:

1、Servlet容器會建立一個請求物件ServletRequst,其中封裝了使用者請求的資訊,以便處理客戶端請求,此外還會建立一個響應物件ServletResponse,用於響應客戶端請求,想客戶端返回資料。

2、然後Servlet容器把建立好的ServletRequst和ServletResponse物件傳給使用者所請求的Servlet。

3、Servlet利用ServletResponse包含的資料和自身的業務邏輯處理請求,並把處理好的結果寫在ServletResponse中,最後Servlet容器把響應結果傳給使用者。

Servlet 的使用

1、自定義Servlet一般需要繼承HttpServlet,而HttpServlet是繼承GenericServlet,而GenericServlet是繼承Servlet。

原因:HttpServlet是特定於HTTP協議的類,Servlet介面和GenericServlet是不特定於任何協議的。Servlet介面中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet介面。而HttpServlet繼承於GenericServlet,因此HttpServlet也實現了Servlet介面。所以我們定義Servlet的時候只需要繼承HttpServlet即可。在HttpServlet中實現了service()方法,並將請求ServletRequest,ServletResponse強轉為HttpRequest和HttpResponse。

2、請求的實際處理者是doGet,doPost方法,自定義Servlet類需要重寫doGet,doPost方法。

基類HttpServlet中的doGet,doPost方法並沒有具體實現,只給出一些異常處理,返回的是錯誤資訊。所以具體的處理程式碼我們需要自己寫。

3、什麼操作需要在doGet方法處理,什麼操作需要在doPut方法處理?

Get方式的請求:直接在瀏覽器位址列輸入訪問的地址所傳送的請求或表單傳送時沒有指明post形式傳送的(表單預設為get提交)都會呼叫doGet方法。

分析:

1)由於get方式請求會將請求引數的名和值轉換成字串,並附在原URl之後,因此可以在位址列上看見請求引數的名和值,安全性比較差。

2)get請求的資料量比較小。

3)只能傳遞字串,不能傳遞二進位制資料

4)伺服器隨機接受GET方法的資料,一旦斷電等原因,伺服器也不知道資訊是否傳送完畢

Post方式的請求:表單以post方式傳送。

分析:

1)POST方式傳送的請求引數以及對應的值放在html header中傳輸,安全性相對較高。

2)post傳遞的資料量比較大,通常認為請求引數大小不受限制,但往往取決於伺服器端的限制。

3)傳遞資料的型別沒有限制,可以傳遞二進位制資料

4)而且此外,Post方法接受資料時,伺服器先接受資料資訊的長度,然後再接受資料,使用post方式,伺服器可以知道資料是否完整。

常用的使用方法:

通常都使用dopost方法,之後再doGet方法呼叫doPost方法即可

圖3。。。

JSP 工作原理

結合右邊給出的流程圖:

當客戶端瀏覽器向伺服器請求一個 JSP 頁面時,伺服器收到該請求後,首先檢查所請求的這個JSP 檔案內容 ( 程式碼 ) 是否已經被更新,或者是否是 JSP 檔案建立後的第一次被訪問:

--如果是,那麼,這個 JSP 檔案就會在伺服器端的 JSP 引擎作用下轉化為一個 Servlet 類的 Java 原始碼檔案。緊接著,這個 Servlet 類會在 Java 編譯器的作用下被編譯成一個位元組碼檔案,並裝載到 jvm 解釋執行。剩下的就等同於 Servlet 的處理過程了。

--如果被請求的 JSP 檔案內容 ( 程式碼 ) 沒有被修改,那麼它的處理過程也等同於一個 Servlet 的處理過程。即直接由伺服器檢索出與之對應的 Servlet 例項來處理。

需要注意的是,JSP 檔案不是在伺服器啟動的時候轉換成 Servlet 類的。而是在被客戶端訪問的時候才可能發生轉換的 ( 如 JSP 檔案內容沒有被更新等,就不再發生 Servlet 轉換 )。
就 Tomcat 而言,開啟目錄 %Tomcat%/work/%您的工程檔案目錄%,然後會看到裡面有 3個子目錄:org/apache/jsp,若沒有這 3 個目錄,說明專案的 JSP 檔案還沒有被訪問過,開啟進到 jsp 目錄下,會看到一些 *_jsp.java 和 *_jsp.class 檔案,這就是 JSP 檔案被轉換成
Servlet 類的原始檔和位元組碼檔案了。
有興趣的話,可以使用瀏覽器訪問伺服器中的 JSP,然後觀察 JSP 轉換 Servlet 以及編譯的時機。

Servlet 與 JSP

JSP 本質是一個 Servlet,它的執行也需要容器的支援。
在 JSP 和 Servlet 檔案中都可以編寫 Java 和 HTML 程式碼,不同的是,
Servlet 雖然也可以動態的生成頁面內容,但更加偏向於邏輯的控制。
JSP 最終被轉換成 Servlet 在 jvm 中解釋執行,在 JSP 中雖然也可以編寫 Java 程式碼,但它更加偏向於頁面檢視的展現。

在 MVC 架構模式中,就 JSP 和 Servlet 而言,C 通常由 Servlet 充當,V 通常由 JSP 來充當。