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

问题:

假设我有一个webserver,它拥有许多servlet。对于在这些servlet中传递的信息,我正在设置会话和实例变量。
现在,如果两个或更多的用户向该服务器发送请求,那么会话变量会发生什么情况?它们对于所有用户来说都是通用的,或者对于每个用户来说都是不同的。如果它们不同,那么服务器如何区分不同的用户呢?
还有一个类似的问题,如果有n用户访问特定的servlet,那么这个servlet只能在第一个用户访问它或者是为所有用户分别实例化时被实例化?换句话说,实例变量会发生什么?

回答:

ServletContext中

当Servlet容器(如Apache Tomcat)启动时,它将部署和加载其所有Web应用程序。加载Web应用程序时,servlet容器将创建一次ServletContext并将其保存在服务器的内存中。网页应用的web.xml文件被解析,每个<servlet><filter><listener>分别发现(或分别用@WebServlet@WebFilter@WebListener注释的每个类)被实例化一次并保存在服务器的内存也是如此。对于每个实例过滤器,其init()方法立即被调用。
当servlet容器关闭时,它卸载所有Web应用程序,调用所有已初始化的servlet和过滤器的destroy()方法,所有ServletContextServletFilterListener实例都将被删除。
Servlet<servlet><load-on-startup>@WebServlet(loadOnStartup)值大于0时,其init()方法也会在启动过程中立即被调用。那些servlet按照该值指定的相同顺序进行初始化(1 – > 1st,2 – > 2nd等)。如果为多个servlet指定了相同的值,那么这些servlet中的每一个按照他们在web.xml@WebServlet类加载中出现的顺序加载。如果不存在“启动启动”值,则只要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并/或HttpSession首次通过request.getSession()获取时,servlet容器创建一个新的HttpSession对象,生成一个长而唯一的ID(可以获得通过session.getId()),并将其存储在服务器的内存中。 servlet容器还在HTTP响应的Set-Cookie头中设置了一个Cookie,其中JSESSIONID作为其名称,唯一的会话ID作为其值。
根据HTTP cookie specification(一个合适的网络浏览器和Web服务器必须遵守的合同),客户端(网络浏览器)需要在Cookie标题中的后续请求中发送此cookie,只要cookie是有效的(即,唯一ID必须引用未到期的会话,域和路径是正确的)。使用浏览器的内置HTTP流量监视器,您可以验证cookie是否有效(请在Chrome / Firefox 23+ / IE9 +中按F12,然后选中Net/Network选项卡)。 servlet容器将检查每个传入的HTTP请求的Cookie头,以了解名称为JSESSIONID的cookie,并使用其值(会话ID)从服务器的内存中获取关联的HttpSession
HttpSession保持活动状态,直到它不被使用超过<session-timeout>中指定的超时值,web.xml中的设置。超时值默认为30分钟。因此,当客户端不访问Web应用程序超过指定的时间时,servlet容器将会删除该会话。即使使用指定的cookie,每个后续请求都将无法访问同一会话; servlet容器将创建一个新的会话。
在客户端,只要浏览器实例运行,会话cookie保持活动状态。因此,如果客户端关闭浏览器实例(所有标签/窗口),则会话在客户端被丢弃。在新的浏览器实例中,与会话相关联的cookie将不存在,因此不再发送。这将导致一个全新的HTTPSession被创建,一个全新的会话cookie开始使用。

简而言之

  • 只要网络应用程序生活,ServletContext就会生活。它在all会话中的all个请求之间共享。
  • 只要客户端与具有相同浏览器实例的Web应用程序进行交互,HttpSession就会生效,并且会话没有在服务器端超时。它在same会话中的all个请求之间共享。
  • servlet从客户端收到HTTP请求之后,HttpServletRequestHttpServletResponse生效,直到完整的响应(网页)到达。 not在别处分享。
  • 只要网络应用程序生活,所有ServletFilterListener实例都可以生活。它们在all会话中的all请求之间共享。
  • ServletContextHttpServletRequestHttpSession中定义的任何attribute只要对象存在,就会生存。对象本身表示了JSF,CDI,Spring等bean管理框架中的“范围”。这些框架将其作用域bean存储为最接近的匹配范围的attribute

线程安全

也就是说,您的主要关注可能是thread safety。您现在应该知道servlet和过滤器在所有请求之间共享。这是Java的好东西,它是多线程的,不同的线程(读取:HTTP请求)可以使用相同的实例。否则,对于每一个请求重新创建init()destroy()它们将是太贵的。
您还应该意识到,您应该决不将任何请求或会话作用域数据分配为servlet或过滤器的instance变量。它将在其他会话中的所有其他请求之间共享。这是线程安全!下面的例子说明了这一点:

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.
    } 
}

也可以看看:

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: How do servlets work? Instantiation, shared variables and multithreading

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 6 = 1