【譯】Stackoverflow:Java Servlet 工做原理問答

導讀

本文來自stackoverflow的問答,討論了Java Servlet的工做機制,如何進行實例化、共享變量和多線程處理。html

問題:Servlet 是如何工做的?Servlet 如何實例化、共享變量、並進行多線程處理?

假設我有一個運行了大量 Servlet 的 web 服務器。經過 Servlet 之間傳輸信息獲得 Servlet 上下文,並設置 session 變量。java

如今,若是有兩名或更多使用者向這個服務發送請求,接下來 session 變量會發生什麼變化?到底是全部用戶都是用共同的變量?仍是不一樣的用戶使用的變量都不同?若是是後者,服務器如何區分不一樣用戶?git

另外一個類似的問題,若是有 *n* 名用戶訪問一個特定的 Servlet,那麼該 Servlet 是僅在第一個用戶首次訪問的時候實例化,仍是分別爲每一個用戶實例化?github

回答(BalusC):

ServletContext

當 Servlet 容器(好比 Apache Tomcat)啓動後,會部署和加載全部 web 應用。當web 應用被加載,Servlet 容器會建立一次 ServletContext,而後將其保存在服務器的內存中。web 應用的 web.xml 被解析,找到其中全部 servletfilter 和 Listener 或 @WebServlet@WebFilter 和 @WebListener 註解的內容,建立一次並保存到服務器的內存中。對於全部過濾器會當即調用 init()。當 Servlet 容器中止,將卸載全部 web 應用,調用全部初始化的 Servlet 和過濾器的 destroy() 方法,最後回收 ServletContext 和全部 Servlet、Filter 與 Listener 實例。web

當問題中的 Servlet 配置的 load-on-startup 或者 @WebServlet(loadOnStartup) 設置了一個大於 0 的值,則一樣會在啓動的時候當即調用 init() 方法。「load-on-startup」中的值表示那些 Servlet 會以相同順序初始化。若是配置的值相同,會遵循 web.xml 中指定的順序或@WebServlet 類加載的順序。另外,若是不設置 「load-on-startup」 值,init() 方法只在第一次 HTTP 請求命中問題中的 Servlet 時才被調用。apache

HttpServletRequest 與 HttpServletResponse

Servlet 容器附加在一個 web 服務上,這個 web 服務會在某個端口號上監聽 HTTP 請求,在開發環境中這個端口一般爲 8080,生產環境中一般爲 80。當客戶端(web 瀏覽器)發送了一個 HTTP 請求,Servlet 容器會建立新的 HttpServletRequest 和 HttpServletResponse 對象,傳遞給已建立好而且請求的 URL 匹配 url-pattern 的 Filter 和 Servlet 實例中的方法,全部工做都在同一個線程中處理。api

request 對象能夠訪問全部該 HTTP 請求中的信息,例如 request header 和 request body。response 對象爲你提供須要的控制和發送 HTTP 響應方法,例如設置 header 和 body(一般會帶有 JSP 文件中的 HTML 內容)。提交併完成HTTP 響應後,將回收 request 和 response 對象。瀏覽器

HttpSession

當用戶第一次訪問該 web 應用時,會經過 request.getSession() 第一次得到 HttpSession。以後 Servlet 容器將會建立 HttpSession,生成一個惟一的 ID(能夠經過 session.getId() 獲取)並儲存在服務器內存中。而後 Servlet 容器在該次 HTTP 響應的 Set-Cookie 頭部設置一個Cookie,以 JSESSIONID 做爲 Cookie 名字,那個惟一的 session ID 做爲 Cookie 的值。tomcat

按照 HTTP cookie 規則(正常 web 瀏覽器和 web 服務端必須遵循的標準),當 cookie 有效時,要求客戶端(瀏覽器)在後續請求的 Cookie 頭中返回這個 cookie。使用瀏覽器內置的 HTTP 流量監控器,你能夠查看它們(在 Chrome、Firefox23+、IE9+ 中按 F12,而後查看 Net/Network 標籤)。Servlet 容器將會肯定每一個進入的 HTTP 請求的 Cookie 頭中是否存在名爲JSESSIONID 的 cookie,而後用它的值(session ID)從服務端內存中找到關聯的 HttpSession安全

你能夠在 web.xml 中設置 session-timeout ,默認值爲 30 分鐘。超時到達以前 HttpSession會一直存活。因此當客戶端再也不訪問該 web 應用超過 30 分鐘後,Servlet 容器就會回收這個 session。後續每一個請求,即便指定 cookie 名稱也不能再訪問到相同的 session。Servlet 容器會建立一個新的 Cookie

另外一方面,客戶端上的 session cookie 有一個默認存活時間,該事件和該瀏覽器實例運行時間同樣長。因此,當客戶端關閉該瀏覽器實例(全部標籤和窗口)後,這個 session 就會被客戶端回收。新瀏覽器實例再也不發送與該 session 關聯的 cookie。一個新的 request.getSession() 將會返回新的 HttpSession 並設置一個擁有新 session ID 的 cookie。

概述

  • ServletContext 與 web 應用存活時間同樣長。它被全部 session 中的全部請求共享。
  • 只要客戶端一直與相同瀏覽器實例的web應用交互而且沒有超時,HttpSession就會存在。
  • HttpServletRequest 和 HttpServletResponse 的存活時間爲客戶端發送完成到完整的響應(web 頁面)到達的這段時間。不會被其餘地方共享。
  • 全部 ServletFilter 和 Listener 對象在 web 應用運行時都是活躍的。它們被全部 session 中的請求共享。
  • 你設置在 HttpServletRequestHttpServletResponse 和 HttpSession 中的全部屬性在問題中的對象存活時都會一直保持存活。

線程安全

即使如此,你最關心的多是線程安全。你如今應該學習到 Servlet 和 filter 被全部請求共享。那是 Java 的一個優勢,使得多個不一樣線程(讀取 HTTP 請求)可使用同一個實例。不然爲每一個請求從新建立線程的開銷實在過於昂貴。

但你應該也意識到永遠不要將任何 request 或 session 域中的數據賦值給 servlet 或 filter 的實例變量。它將會被全部其餘 session 中的全部請求共享。那是非線程安全的!下面的示例對這種狀況進行了展現:

public class ExampleServlet extends HttpServlet {
 
    private Object thisIsNOTThreadSafe;
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;
 
        thisIsNOTThreadSafe = request.setParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    }
}

請參考:

 


 

原文連接: stackoverflow

首發至: http://www.importnew.com/17025.html,並已同步至 Github,歡迎 Star 關注。

相關文章
相關標籤/搜索