2010-02-02 38 views

回答

177

當Servlet容器啓動時,它:

  1. 讀取web.xml;
  2. 在classpath中查找已聲明的Servlet;和
  3. 加載和實例化每個Servlet 只有一次

粗略地說,像這樣的:

String urlPattern = parseWebXmlAndRetrieveServletUrlPattern(); 
String servletClass = parseWebXmlAndRetrieveServletClass(); 
HttpServlet servlet = (HttpServlet) Class.forName(servletClass).newInstance(); 
servlet.init(); 
servlets.put(urlPattern, servlet); // Similar to a map interface. 

這些Servlet的存儲在內存中,並重復使用,每次請求的URL匹配的Servlet的相關url-pattern。 servlet容器然後執行類似操作的代碼:

for (Entry<String, HttpServlet> entry : servlets.entrySet()) { 
    String urlPattern = entry.getKey(); 
    HttpServlet servlet = entry.getValue(); 
    if (request.getRequestURL().matches(urlPattern)) { 
     servlet.service(request, response); 
     break; 
    } 
} 

GenericServlet#service()它反過來決定哪個的doGet()doPost()等。調用基於HttpServletRequest#getMethod()

您會發現,servletcontainer對每個請求重用相同的servlet實例。換句話說:servlet在之間共享,每個請求。這就是爲什麼以線程安全方式編寫servlet代碼非常重要 - 實際上很簡單:只需執行而不是將請求或會話作用域數據分配爲servlet實例變量,但僅作爲方法局部變量。例如。

public class MyServlet extends HttpServlet { 

    private Object thisIsNOTThreadSafe; 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
     Object thisIsThreadSafe; 

     thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests! 
     thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe. 
    } 
} 
+21

+1。我將補充一點,如果在web.xml中將相同的servlet類映射到兩個不同的URL,那麼會創建兩個實例。但總體原則仍然成立,一個例子服務於多個要求。 – Yoni 2010-02-02 13:55:51

+1

@BalusC,奇怪的是,有沒有一種方法可以從Web應用程序訪問一組已啓動的Servlet? – shabunc 2012-03-21 19:03:55

+1

該類中的變量thisIsNOTThreadSafe是否被共享給不同的用戶,或者只被共享給同一用戶的不同頁面。我的意思是,如果我在計算機中瀏覽此頁面,並在計算機上運行,​​我們是否共享這個非線程安全的相同空間?謝謝。 – overshadow 2014-08-25 10:01:08

27

不,只有一個servlet實例可用於來自多個客戶端的多個請求。這導致了兩條重要規則:

  • 不使用servlet中的實例變量,除了應用程序範圍的值(通常從上下文參數獲得)之外。
  • 不作方法​​在servlet

(同樣適用於Servlet過濾器和JSP)

3

不能有Servlet類的多個實例。即使有一個servlet實例,它也能夠處理多個請求。所以不使用類級變量是明智的。

9

根據Java Servlet規範版本3.0(第6-7頁),將有每JVM聲明一個實例,除非該servlet實現了SingleThreadModel在這種情況下可能存在每個JVM多個實例。

+0

Servlet規範3.(第6-7頁)第2.2.1節有關單線程模型的注意事項,說「SingleThreadModel接口在此版本的規範中已被棄用「。 – Reva 2015-12-22 22:35:05

3

對於那些知道真正的JavaScript(不只是它的庫)的人來說,Servlets可以被看作是函數對象。作爲功​​能對象,他們的主要任務是,而不是存儲一些信息在他們的胸前。沒有必要實例化每個這樣的功能對象的多個實例,其原理與Java類方法在該類的所有實例中共享的原理相同。

3

儘管已經有幾個很好的答案,但他們都沒有提到部署在分佈式環境中的Java Web應用程序。這是一個實際的場景,實際上創建了單個servlet的多個實例。在分佈式環境中,您有一組機器來處理請求,並且請求可以轉到任何這些機器。這些機器中的每一個都應該能夠處理請求,因此每臺機器都應該在其JVM中擁有一個MyAwesomeServlet實例。

因此,正確的語句是每個JVM只有一個實例,除非它實現了SingleThreadModel。

SimpleThreadModel用簡單的話說,每個Servlet實例只需要一個線程,所以基本上你需要爲每個來到的請求創建一個實例來處理它,它基本上殺死了以並行方式處理請求的整個概念並且不被認爲是一種好的做法,因爲在準備好處理請求之前,servlet對象的創建和初始化需要時間。

相關問題