1、先從servlet容器提及:你們最爲熟悉的servlet容器就是Tomcat ,Servlet 容器是如何管理 Servlet?web
先看一下tomcat的容器模型:數據庫
從上圖能夠看出 Tomcat 的容器分爲四個等級,真正管理Servlet 的容器是Context 容器,一個 Context 對應一個 Web 工程編程
Tomcat 的容器等級中,Context 容器是直接管理 Servlet 在容器中的包裝類Wrapper(StandardWrapper)的容器,因此 Context 容器如何運行將直接影響 Servlet 的工做方式。tomcat
這裏解釋一下servlet的包裝類:StandardWrapper,這裏有個疑問,爲何要將 Servlet 包裝成 StandardWrapper 而不直接是 Servlet 對象。由於StandardWrapper 是 Tomcat 容器中的一部分,它具備容器的特徵,而 Servlet 爲一個獨立的 web 開發標準,不該該強耦合在 Tomcat 中。安全
除了將 Servlet 包裝成 StandardWrapper 並做爲子容器添加到 Context 中,其它的全部 web.xml 屬性都被解析到 Context 中,因此說 Context 容器纔是真正運行 Servlet 的 Servlet 容器。一個 Web 應用對應一個 Context 容器,容器的配置屬性由應用的 web.xml 指定,這樣咱們就能理解 web.xml 到底起到什麼做用了服務器
2、下面簡述一下servlet的工做工程:網絡
Web服務器在與客戶端交互時.Servlet的工做過程是:多線程
1. 在客戶端對web服務器發出請求app
2. web服務器接收到請求後將其發送給Servletspa
3. Servlet容器爲此產生一個實例對象並調用ServletAPI中相應的方法來對客戶端HTTP請求進行處理,而後將處理的響應結果返回給WEB服務器.
4. web服務器將從Servlet實例對象中收到的響應結構發送回客戶端.
3、servlet的生命週期:
如上圖所示,Servlet的生命週期能夠分爲四個階段,即裝載類及建立實例階段、初始化階段、服務階段和實例銷燬階段。下面針對每一個階段的編程任務及注意事項進行詳細的說明。
一、建立servlet實例:
在默認狀況下Servlet實例是在第一個請求到來的時候建立,之後複用。若是有的Servlet須要複雜的操做須要載初始化時完成,好比打開文件、初始化網絡鏈接等,能夠通知服務器在啓動的時候建立該Servlet的實例。具體配置以下:
<servlet>
<servlet-name>TimeServlet</servlet-name>
<servlet-class>com.allanlxf.servlet.basic.TimeServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
建立servlet對象的相關類結構:
二、初始化
一旦Servlet實例被建立,Web服務器會自動調用init(ServletConfig config)方法來初始化該Servlet。其中方法參數config中包含了Servlet的配置信息,好比初始化參數,該對象由服務器建立。
I.如何配置Servlet的初始化參數?
在web.xml中該Servlet的定義標記中,好比:
<servlet>
<servlet-name>TimeServlet</servlet-name>
<servlet-class>com.allanlxf.servlet.basic.TimeServlet</servlet-class>
<init-param>
<param-name>user</param-name>
<param-value>username</param-value>
</init-param>
<init-param>
<param-name>blog</param-name>
<param-value>http://。。。</param-value>
</init-param>
</servlet>
配置了兩個初始化參數user和blog它們的值分別爲username和http://。。。, 這樣之後要修改用戶名和博客的地址不須要修改Servlet代碼,只需修改配置文件便可。
II.如何讀取Servlet的初始化參數?
ServletConfig中定義了以下的方法用來讀取初始化參數的信息:
public String getInitParameter(String name)
參數:初始化參數的名稱。
返回:初始化參數的值,若是沒有配置,返回null。
III.init(ServletConfig)方法執行次數
在Servlet的生命週期中,該方法執行一次。
IV.init(ServletConfig)方法與線程
該方法執行在單線程的環境下,所以開發者不用考慮線程安全的問題。
V.init(ServletConfig)方法與異常
該方法在執行過程當中能夠拋出 ServletException來通知Web服務器Servlet實例初始化失敗。一旦ServletException拋出,Web服務器不會將客戶 端請求交給該Servlet實例來處理,而是報告初始化失敗異常信息給客戶端,該Servlet實例將被從內存中銷燬。若是在來新的請求,Web服務器會 建立新的Servlet實例,並執行新實例的初始化操做
三、服務
一旦Servlet實例成功建立及初始化,該Servlet實例就能夠被服務器用來服務於客戶端的請求並生成響應。在服務階段Web服務器會調用該實例的 service(ServletRequest request, ServletResponse response)方法,request對象和response對象有服務器建立並傳給Servlet實例。request對象封裝了客戶端發往服務器端 的信息,response對象封裝了服務器發往客戶端的信息。
I. service()方法的職責
service()方法爲Servlet的核心方法,客戶端的業務邏輯應該在該方法內執行,典型的服務方法的開發流程爲:
解析客戶端請求-〉執行業務邏輯-〉輸出響應頁面到客戶端
II.service()方法與線程
爲了提升效率,Servlet規範要求一個Servlet實例必須可以同時服務於多個客戶端請求,即service()方法運行在多線程的環境下,Servlet開發者必須保證該方法的線程安全性。
III.service()方法與異常
service()方法在執行的過程當中能夠拋出ServletException和IOException。其中ServletException能夠在 處理客戶端請求的過程當中拋出,好比請求的資源不可用、數據庫不可用等。一旦該異常拋出,容器必須回收請求對象,並報告客戶端該異常信息。 IOException表示輸入輸出的錯誤,編程者沒必要關心該異常,直接由容器報告給客戶端便可。
編程注意事項說明:
1) 當Server Thread線程執行Servlet實例的init()方法時,全部的Client Service Thread線程都不能執行該實例的service()方法,更沒有線程可以執行該實例的destroy()方法,所以Servlet的init()方法 是工做在單線程的環境下,開發者沒必要考慮任何線程安全的問題。
2) 當服務器接收到來自客戶端的多個請求時,服務器會在單獨的Client Service Thread線程中執行Servlet實例的service()方法服務於每一個客戶端。此時會有多個線程同時執行同一個Servlet實例的 service()方法,所以必須考慮線程安全的問題。
3) 請你們注意,雖然service()方法運行在多線程的環境下,並不必定要同步該方法。而是要看這個方法在執行過程當中訪問的資源類型及對資源的訪問方式。分析以下:
i. 若是service()方法沒有訪問Servlet的成員變量也沒有訪問全局的資源好比靜態變量、文件、數據庫鏈接等,而是隻使用了當前線程本身的資源, 好比非指向全局資源的臨時變量、request和response對象等。該方法自己就是線程安全的,沒必要進行任何的同步控制。
ii. 若是service()方法訪問了Servlet的成員變量,可是對該變量的操做是隻讀操做,該方法自己就是線程安全的,沒必要進行任何的同步控制。
iii. 若是service()方法訪問了Servlet的成員變量,而且對該變量的操做既有讀又有寫,一般須要加上同步控制語句。
iv. 若是service()方法訪問了全局的靜態變量,若是同一時刻系統中也可能有其它線程訪問該靜態變量,若是既有讀也有寫的操做,一般須要加上同步控制語句。
v. 若是service()方法訪問了全局的資源,好比文件、數據庫鏈接等,一般須要加上同步控制語句。
四、銷燬
當Web服務器認爲Servlet實例沒有存在的必要了,好比應用從新裝載,或服務器關閉,以及Servlet很長時間都沒有被訪問過。服務器能夠從內存 中銷燬(也叫卸載)該實例。Web服務器必須保證在卸載Servlet實例以前調用該實例的destroy()方法,以便回收Servlet申請的資源或 進行其它的重要的處理。
Web服務器必須保證調用destroy()方法以前,讓全部正在運行在該實例的service()方法中的線程退出或者等待這些線程一段時間。一旦 destroy()方法已經執行,Web服務器將拒絕全部的新到來的對該Servlet實例的請求,destroy()方法退出,該Servlet實例即 能夠被垃圾回收。
4、servlet解析客戶端http請求流程圖:
1. web客戶向Servlet容器發出HTTP請求;
2. Servlet容器解析web的HTTP請求.
3. Servlet容器建立一個HttpRequest對象,在這個對象中封裝了http請求信息;
4. Servlet容器建立一個HttpResponse對象;
5. Servlet容器(若是訪問的該servlet不是在服務器啓動時建立的,則先建立servlet實例並調用init()方法初始化對象)調用HttpServlet的service()方法,把HttpRequest和HttpResponse對象爲service方法的參數傳給HttpServlet對象;
6. HttpServlet調用HttpRequest的有關方法,獲取HTTP請求信息;
7. HttpServlet調用HttpResponse的有關方法,生成響應數據;
8. Servlet容器把HttpServlet的響應結果傳給web客戶.