servlets如何工作?实例化,会话,共享变量和多线程

问题

假设,我有一个拥有众多servlet的web服务器。对于在这些servlet之间传递的信息,我设置了会话和实例变量。

现在,如果有2个或更多用户向该服务器发送请求,那么会话变量会发生什么变化?它们对于所有的用户都是共同的吗?或者对于每个用户来说它们都会有所不同。如果它们不同,那么服务器如何能够区分不同的用户?

还有一个类似的问题,如果有'n`用户访问一个特定的servlet,那么这个servlet只在第一个用户第一次访问它时才被实例化,或者它是否分别为所有用户实例化了?换句话说,实例变量会发生什么?


#1 热门回答(1606 赞)

ServletContext

当servlet容器(如Apache Tomcat)启动时,它将部署并加载其所有Web应用程序。当一个web应用程序被加载时,servlet容器创建一次ServletContext并将其保存在服务器的内存中。解析了web应用程序的web.xml文件,找到了每个<servlet><filter><listener>(或者每个用@WebServlet@WebFilter@注释的类。 WebListener“)被实例化一次并保存在服务器的内存中。对于每个实例化的过滤器,它的init()方法都用一个新的FilterConfig`调用。

当servlet容器关闭时,它卸载所有的web应用程序,调用所有初始化的servlet和过滤器的destroy()方法,并且所有的ServletContextServletFilterListener实例都被抛弃。

Servlet<servlet> <load-on-startup>@WebServlet(loadOnStartup)的值大于'0时,它的init()方法也会在启动时用新的`` ServletConfig。这些servlet按照该值指定的相同顺序进行初始化(1 - > 1st,2 - > 2nd等)。如果为多个servlet指定了相同的值,那么每个servlet都会按照它们出现在“web.xml”或“@ WebServlet”类加载中的顺序加载。如果没有“load-on-startup”值,只要HTTP请求第一次访问该servlet,就会调用init()方法。

HttpServletRequest和HttpServletResponse

Servlet容器连接到一个Web服务器,该服务器在某个端口号上侦听HTTP请求(端口8080通常在开发过程中使用,在生产过程中使用端口80)。当一个客户端(带有Web浏览器的用户)发送一个HTTP请求时,servlet容器会创建新的HttpServletRequestHttpServletResponse对象,并将它们传递给任何已定义的Filter链,最后是Servlet实例。

filters的情况下,调用doFilter()方法。当它的代码调用chain.doFilter(request,response)时,请求和响应继续到下一个过滤器,或者如果没有剩余的过滤器,则点击servlet。

servlets的情况下,调用service()方法。默认情况下,这个方法根据request.getMethod()来决定调用哪个doXxx()方法。如果确定的方法在servlet中不存在,则在响应中返回一个HTTP 405错误。

请求对象提供对所有关于HTTP请求的信息的访问,例如它的标题和正文。响应对象提供了以您想要的方式控制和发送HTTP响应的功能,例如,允许您设置标题和正文(通常使用JSP文件中生成的HTML内容)。当HTTP响应被提交并完成时,请求和响应对象都被回收并重新使用。

HttpSession

当客户端首次访问webapp并且/或者通过request.getSession()第一次获得HttpSession时,servlet容器创建一个新的HttpSession对象,生成一个长且唯一的ID你可以通过session.getId()获得),并将其存储在服务器的内存中。 servlet容器还在HTTP响应的Set-Cookie标头中设置了一个Cookie,它的名字是JSESSIONID,唯一的会话ID是它的值。

根据HTTP cookie specification(一个合适的Web浏览器和Web服务器必须遵守的合同),只要cookie有效,客户端(Web浏览器)就需要在“Cookie”标头中的后续请求中发回此cookie。 (即,唯一ID必须指未到期的会话,并且域和路径是正确的)。使用浏览器的内置HTTP流量监视器,可以验证cookie是否有效(在Chrome / Firefox 23 / IE9中按F12键,然后检查网络/网络选项卡)。 servlet容器将检查每个传入HTTP请求的Cookie头是否存在名为JSESSIONID的cookie,并使用其值(会话ID)从服务器的内存中获取关联的HttpSession

'HttpSession'一直保持活动状态,直到它没有超过<session-timeout>中指定的超时值,web.xml中的一个设置。超时值默认为30分钟。因此,当客户端访问Web应用程序的时间超过指定的时间时,servlet容器会破坏会话。每一个后续的请求,即使是指定的cookie,都不会再访问同一个会话;该servlet容器将创建一个新的会话。

在客户端,只要浏览器实例正在运行,会话cookie就会保持活动状态。所以,如果客户端关闭浏览器实例(所有标签/窗口),那么会话在客户端被丢弃。在新的浏览器实例中,与会话关联的cookie将不存在,因此不会再发送。这会导致创建一个全新的HTTPSession,并开始全新的会话Cookie用过的。

简而言之

  • 只要Web应用程序存在,ServletContext就会一直存在。它在所有会议的所有请求中共享。
  • 只要客户端使用相同的浏览器实例与Web应用程序进行交互,HttpSession就会一直存在,并且会话在服务器端没有超时。它在同一会话中的所有请求中共享。
  • HttpServletRequest和HttpServletResponse从servlet接收到来自客户端的HTTP请求的时间起直到完成响应(网页)到达。它不在别处分享。
  • 只要Web应用程序存在,所有Servlet,过滤器和侦听器实例都会生效。它们在所有会议的所有请求中共享。
  • 只要有问题的对象存在,ServletContext,HttpServletRequest和HttpSession中定义的任何属性都将存在。该对象本身代表了bean管理框架(如JSF,CDI,Spring等)中的“范围”。这些框架将其作用域bean存储为最接近匹配范围的属性。

##线程安全

也就是说,您的主要担忧可能是线程安全。您现在应该知道,servlet和过滤器是在所有请求之间共享的。这是Java的好处,它是多线程的,不同的线程(读取:HTTP请求)可以使用同一个实例。否则,为每个请求重新创建init()destroy()它们会非常昂贵。

您还应该意识到,您应该将任何请求或会话作用域数据作为servlet或过滤器的实例变量分配.永远不会。它将在其他会议中的所有其他请求之间共享。那不是防线!以下示例说明了这一点:

public class ExampleServlet 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.
    } 
}

也可以看看:

  • JSF,Servlet和JSP有什么区别?
  • Java中会话管理的最佳选择
  • servlet映射url模式中的/和/ *之间的区别
  • Servlet中的doGet和doPost
  • Servlet似乎同步处理多个并发浏览器请求
  • 为什么Servlet不是线程安全的?

#2 热门回答(393 赞)

##会话

enter image description here

enter image description here

简而言之:Web服务器在访问者访问时向每个访问者发出一个唯一的标识符。访客必须带回该身份证以便他下次得到认可。此标识符还允许服务器正确地将一个会话拥有的对象与另一个会话拥有的对象隔离。

Servlet实例化

Ifload-on-startupisfalse

enter image description here

enter image description here

Ifload-on-startupistrue

enter image description here

enter image description here

一旦他处于服务模式和沟槽上,同一个服务器将处理来自所有其他客户的请求。

enter image description here

为什么每个客户端都有一个实例不是一个好主意?想想这个:你会为每一个订单雇佣一个比萨饼人吗?做到这一点,你很快就会倒闭。

它虽然有一个小风险。记住:这个单例把所有的订单信息放在口袋里:所以如果你对thread safety on servlets不是很谨慎,他可能会把错误的订单交给某个客户。


#3 热门回答(39 赞)

Java servlets中的会话与其他语言(如PHP)中的会话相同。这对用户来说是独一无二的。服务器可以用不同的方式跟踪它,例如cookie,url重写等。这个Java docarticle在Java servlet的上下文中解释了它,并且指出会话的确切维护方式是给服务器设计者留下的实现细节。该规范仅规定,它必须通过与服务器的多个连接对用户保持唯一性。有关您的两个问题的详细信息,请查看this article from Oracle

编辑如何使用servlet中的会话有一个很好的教程here。 Andhere是Sun关于Java Servlets的章节,它们是什么以及如何使用它们。在这两篇文章之间,你应该能够回答你所有的问题。