1. 概述
在這篇簡短的文章中,我們將從概念上理解什麼是servlet
和 servlet
容器以及它們是如何工作的。
同時,還能在請求、響應、會話物件、共享變數和多執行緒的上下文中看到它們的身影。
2. Servlets 和 它的容器
servlet
是 JEE
用於 web
開發常用的元件。它們基本上是執行在容器邊界內的Java程式。總的來說,它們負責接受請求,處理請求,並返回響應。
要使用它們,首先需要容器註冊 servlet
,無論是基於 JEE
還是基於 Spring
的容器,都可以在啟動時接收它們。在開始時,容器通過呼叫 init() 方法來例項化 servlet
。
初始化完成後,servlet
就可以接受傳入的請求。隨後,容器將這些請求定向到 servlet
的 service
方法中進行處理。之後,它根據HTTP請求型別將請求進一步委託給適當的方法,例如 doGet()
或 doPost()
。
使用 destroy()
,容器會銷燬 servlet
,並且不再接受傳入的請求。我們將這個 init-service-destroy
的迴圈稱為 servlet
的生命週期。
現在我們從容器的角度來看,比如 Apache Tomcat
或 Jetty
在啟動時,建立一個 ServletContext 的物件,ServletContext 的任務是充當伺服器或容器的記憶體,並記住與web應用程式相關聯的所有servlet、過濾器和偵聽器,如其 web.xml檔案或等效註解。在容器停止之前,ServletContext 會一直保留它。
不管怎麼說,servlet
的 load-on-startup
引數扮演重要的角色 。如果此引數的值大於零,則只有在啟動時伺服器才會對其進行初始化。如果未指定此引數,則在請求第一次命中 servlet
時呼叫它的 init()。
3. Request, Response 和 Session
在上一節中,我們討論了傳送請求和接收響應,這基本上是任何CS應用程式的基礎。現在,我們從servlet
的角度來詳細瞭解它們。
在這種情況下,請求將由 HttpServletRequest
表示,響應將用 HttpServletResponse
表示。
每當瀏覽器或curl命令等傳送請求時,容器都會建立一個新的 HttpServletRequest
和 HttpServletResponse
物件。然後將這些新物件傳遞給 servlet
的 service
方法。基於 HttpServletRequest
的 method
屬性,此方法確定應呼叫哪個 doXXX
方法。
除了有關方法的資訊外,request物件還攜帶其他資訊,如頭、引數和主體。類似地,HttpServletResponse
物件也攜帶頭、引數和主體——我們可以在 servlet
的 doXXX
方法中設定它們。
這些物件的生命稍縱即逝。當客戶端獲得響應時,伺服器將標記用於垃圾回收的請求和響應物件。
那麼我們如何在隨後的客戶端請求或連線之間保持一個狀態?答案就是 HttpSession
。
原理是將這些物件繫結到使用者會話,以便與特定使用者相關的資訊可以跨多個請求持久化。這通常是通過使用cookies的概念,使用 [JSESSIONID] 作為給定會話的唯一識別符號。我們可以在web.xml中指定會話的超時時長。
<session-config>
<session-timeout>10</session-timeout>
</session-config>
以上配置表示,如果會話空閒了10分鐘,伺服器將丟棄它。任何後續請求都將建立一個新的會話。
4. Servlets 共享資料
根據所需的範圍,servlet
可以通過多種方式共享資料。
正如在前面的章節中提到的,不同的物件有不同的生命週期。HttpServletRequest
和HttpServletResponse
物件只存在於一個 servlet
呼叫之間。HttpSession
只要它處於活動狀態並且沒有超時,它就會一直存在。
ServletContext
的生命週期最長。它與Web應用程式一起誕生,只有當應用程式本身關閉時才會被銷燬。由於servlet、filter 和 listener 例項與上下文繫結,所以只要web應用程式啟動並執行,它們也會一直存在。
因此,如果我們的需求是在所有servlet之間共享資料,假設我們要計算站點的訪問者數量,那麼我們應該將變數放在 ServletContext 中。如果我們需要在一個會話中共享資料,那麼我們就把它儲存在會話範圍內。在本例中,使用者名稱就是一個例子。
最後,還有與單個請求的資料相關的請求範圍,比如請求有效負載。
5. 處理多執行緒
多個HttpServletRequest
物件彼此共享 servlet
,這樣每個請求都使用它自己的 servlet
例項執行緒進行操作。
就執行緒安全而言,這實際上表明:我們不應該將請求或會話範圍內的資料指定為 servlet
的例項變數。
例如,下面的程式碼片段:
public class ExampleThree extends HttpServlet {
private String instanceMessage;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String message = request.getParameter("message");
instanceMessage = request.getParameter("message");
request.setAttribute("text", message);
request.setAttribute("unsafeText", instanceMessage);
request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response);
}
}
在本例中,會話中的所有請求共享 instanceMessage
,而 message
對於給定的請求物件是唯一的。因此,在併發請求的情況下,instanceMessage 中的資料可能不一致。
6. 總結
在本教程中,我們探討了有關servlet的一些概念、它們的容器以及它們所圍繞的一些基本物件,以及 servlet 如何共享資料和多執行緒如何影響它們.
如果你覺得文章還不錯,記得關注公眾號: 鍋外的大佬
鍋外的大佬部落格