servlet 有良好的生存期的定義,包括如何加載、實例化、初始化、處理客戶端請求以及如何被移除。這個生存期由 javax.servlet.Servlet 接口的 init,service 和 destroy 方法表達.
1 、加載和實例化
首先容器必須先定位 servlet 類,在必要的情況下,容器使用通常的 Java 類加載工具加載該 servlet ,可能是從本機文件系統,也可以是從遠程 文件系統甚至其它的網絡服務。容器加載 servlet 類以後,它會實例化該類的一個實例。需要注意的是可能會實例化多個實例,例如一個 servlet 類因 爲有不同的初始參數而有多個定義,或者 servlet 實現 SingleThreadModel 而導致容器爲之生成一個實例池。
2 、初始化
servlet 加載並實例化後,容器必須在它能夠處理客戶端請求前初始化它。初始化的過程主要是讀取永久的配置信息,昂貴資源(例如 JDBC 連接)以及 其它僅僅需要執行一次的任務。通過調用它的 init 方法並給它傳遞唯一的一個(每個 servlet 定義一個) ServletConfig 對象完成這個過 程。給它傳遞的這個配置對象允許 servlet 訪問容器的配置信息中的名稱-值對( name-value )初始化參數。這個配置對象同時給 servlet 提供了訪問實現了 ServletContext 接口的具體對象的方法,該對象描述了 servlet 的運行環境。
2.1 初始化的錯誤處理
在初始化期間, servlet 實例可能通過拋出 UnavailableException 或者 ServletException 異常表明它不能進行有效服務。如果一個 servlet 拋出一個這樣的異常,它將不會被置入有效服務並且應該被容器立即釋 放。在此情況下 destroy 方法不會被調用因爲初始化沒有成功完成。在失敗的實例被釋放後,容器可能在任何時候實例化一個新的實例,對這個規則的唯一例 外是如果失敗的 servlet 拋出的異常是 UnavailableException 並且該異常指出了最小的無效時間,那麼容器就會至少等待該時間指明的 時限纔會重新試圖創建一個新的實例。
2.2 、工具因素
當工具(注:根據筆者的理解,這個工具可能是應用服務器的 某些檢查工具,通常是驗證應用的合法性和完整性)加載和內省( introspect )一個 web 應用時,它可能加載和內省該應用中的類,這個行爲將觸發那 些類的靜態初始方法被執行,因此,開發者不能假定只要當 servlet 的 init 方法被調用後它才處於活動容器運行狀態( active container runtime )。作爲一個例子,這意味着 servlet 不能在它的靜態(類)初始化方法被調用時試圖建立數據庫連接或者連接 EJB 容器。
3 、處理請求
在 servlet 被適當地初始化後,容器就可以使用它去處理請求了。每一個請求由 ServletRequest 類型的對象代表,而 servlet 使用 ServletResponse 迴應該請求。這些對象被作爲 service 方法的參數傳遞給 servlet 。在 HTTP 請求的情況下,容器必須提供代表請 求和迴應的 HttpServletRequest 和 HttpServletResponse 的具體實現。需要注意的是容器可能會創建一個 servlet 實 例並將之放入等待服務的狀態,但是這個實例在它的生存期中可能根本沒有處理過任何請求。
3.1 、多線程問題
容器 可能同時將多個客戶端的請求發送給一個實例的 service 方法,這也就意味着開發者必須確保編寫的 servlet 可以處理併發問題。如果開發者想防止這 種缺省的行爲,那麼他可以讓他編寫的 servlet 實現 SingleThreadModel 。實現這個類可以保證一次只會有一個線程在執行 service 方法並且一次性執行完。容器可以通過將請求排隊或者維護一個 servlet 實例池滿足這一點。如果 servlet 是分佈式應用的一部分,那麼,那麼容器可 能在該應用分佈的每個 JVM 中都維護一個實例池。如果開發者使用 synchronized 關鍵字定義 service 方法 ( 或者是 doGet 和 doPost) ,容器將排隊處理請求,這是由底層的 java 運行時系統要求的。我們強烈推薦開發者不要同步 service 方法或者 HTTPServlet 的諸如 doGet 和 doPost 這樣的服務方法。
3.2 、處理請求中的異常
servlet 在對請求進行服務的時 候有可能拋出 ServletException 或者 UnavailableException 異常。 ServletException 表明在處理請求的過 程中發生了錯誤容器應該使用合適的方法清除該請求。 UnavailableException 表明 servlet 不能對請求進行處理,可能是暫時的,也可 能是永久的。如果 UnavailableException 指明是永久性的,那麼容器必須將 servlet 從服務中移除,調用它的 destroy 方法並釋 放它的實例。如果指明是暫時的,那麼容器可以選擇在異常信息裏面指明的這個暫時無法服務的時間段裏面不向它發送任何請求。在這個時間段裏面被被拒絕的請求 必須使用 SERVICE_UNAVAILABLE (503) 返回狀態進行響應並且應該攜帶稍後重試( Retry-After )的響應頭表明不能服務只是暫時的。容器也可以選擇不對暫時性和永久性的不可用 進行區分而全部當作永久性的並移除拋出異常的 servlet 。
容器沒有被要求將一個加載的 servlet 保存多長時間,因此一個 servlet 實例可能只在容器中存活了幾毫秒,當然也可能是其它更長的任意時間(但是 肯定會短於容器的生存期)當容器決定將之移除時(原因可能是保存內存資源或者自己被關閉),那麼它必須允許 servlet 釋放它正在使用的任何資源並保存 任何永久狀態(這個過程通過調用 destroy 方法達到)。容器在能夠調用 destroy 方法前,它必須允許那些正在 service 方法中執行的線程執行 完或者在服務器定義的一段時間內執行(這個時間段在容器調用 destroy 之前)。一旦 destroy 方法被調用,容器就不會再向該實例發送任何請求。如 果容器需要再使用該 servlet ,它必須創建新的實例。 destroy 方法完成後,容器必須釋放 servlet 實例以便它能夠被垃圾回收。