1. 程式人生 > >誰再問Servlet的問題,我就親自上門來教學了

誰再問Servlet的問題,我就親自上門來教學了

## 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()*。 ![file](https://img2020.cnblogs.com/other/1692986/202012/1692986-20201218090033403-1766027555.jpg) ## 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中指定會話的超時時長。 ```xml ``` 以上配置表示,如果會話空閒了10分鐘,伺服器將丟棄它。任何後續請求都將建立一個新的會話。 ## 4. Servlets 共享資料 根據所需的範圍,`servlet` 可以通過多種方式共享資料。 正如在前面的章節中提到的,不同的物件有不同的生命週期。`HttpServletRequest`和`HttpServletResponse` 物件只存在於一個 `servlet` 呼叫之間。`HttpSession`只要它處於活動狀態並且沒有超時,它就會一直存在。 `ServletContext`的生命週期最長。它與Web應用程式一起誕生,只有當應用程式本身關閉時才會被銷燬。由於servlet、filter 和 listener 例項與上下文繫結,所以只要web應用程式啟動並執行,它們也會一直存在。 因此,如果我們的需求是在所有servlet之間共享資料,假設我們要計算站點的訪問者數量,那麼我們應該將變數放在 *ServletContext* 中。如果我們需要在一個會話中共享資料,那麼我們就把它儲存在會話範圍內。在本例中,使用者名稱就是一個例子。 最後,還有與單個請求的資料相關的請求範圍,比如請求有效負載。 ## 5. 處理多執行緒 多個`HttpServletRequest` 物件彼此共享 `servlet`,這樣每個請求都使用它自己的 `servlet` 例項執行緒進行操作。 就執行緒安全而言,這實際上表明:我們不應該將請求或會話範圍內的資料指定為 `servlet`的例項變數。 例如,下面的程式碼片段: ```java 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 如何共享資料和多執行緒如何影響它們. 如果你覺得文章還不錯,記得關注公眾號: 鍋外的大佬 [ 鍋外的大佬部落格](http://developlee.top)