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

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

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

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


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实例。

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

在servlet的情况下,调用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规范(一个体面的Web浏览器和Web服务器必须遵守的合同),客户端(Web浏览器)需要在Cookie标头中的后续请求中将该cookie发回,只要cookie是有效(即唯一ID必须指未到期的会话,域和路径是正确的)。 使用浏览器的内置HTTP流量监控器,您可以验证cookie是否有效(在Chrome / Firefox 23+ / IE9 +中按F12,并检查网络/网络选项卡)。 servlet容器将检查每个传入HTTP请求的Cookie标头是否存在名为JSESSIONID的cookie,并使用其值(会话标识)从服务器内存中获取关联的HttpSession

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

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

简而言之

  • 只要Web应用程序存在, ServletContext一直存在。 它在所有会议的所有请求中共享。
  • 只要客户端使用相同的浏览器实例与Web应用程序进行交互,并且会话在服务器端没有超时, HttpSession一直存在。 它在同一会话中的所有请求中共享。
  • HttpServletRequestHttpServletResponse从servlet接收到来自客户端的HTTP请求的时间起直到完成响应(网页)到达。 它不在其他地方共享。
  • 所有的ServletFilterListener实例只要Web应用程序存在就存在。 它们在所有会议的所有请求中共享。
  • 任何在ServletContextHttpServletRequestHttpSession定义的attribute都会在所讨论的对象存在的前提下存在。 该对象本身代表了bean管理框架(如JSF,CDI,Spring等)中的“范围”。这些框架将其作用域bean存储为最接近匹配范围的attribute
  • 线程安全

    也就是说,你的主要担心可能是线程安全。 您现在应该知道,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不是线程安全的?

  • 会议

    在这里输入图像描述在这里输入图像描述

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

    Servlet实例化

    如果load-on-startupfalse

    在这里输入图像描述在这里输入图像描述

    如果load-on-startup

    在这里输入图像描述在这里输入图像描述

    一旦他处于服务模式和沟槽中,相同的servlet将处理来自所有其他客户端的请求。

    在这里输入图像描述

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

    它虽然有一个小风险。 请记住:这个人将所有的订单信息放在口袋里:所以如果你对Servlet的线程安全性不够谨慎,他最终可能会向某个客户提供错误的订单。


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

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

    链接地址: http://www.djcxy.com/p/76561.html

    上一篇: How do servlets work? Instantiation, sessions, shared variables and multithreading

    下一篇: Java EE web development, where do I start and what skills do I need?