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

假设,我有一个拥有大量servlet的Web服务器.对于在这些servlet之间传递的信息,我正在设置会话和实例变量.

现在,如果有2个或更多用户向此服务器发送请求,那么会话变量会发生什么?它们对所有用户都是通用的,或者对于每个用户而言都是不同的.如果它们不同,那么服务器如何区分不同的用户?

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

最佳答案
ServletContext中

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

当Servlet具有< servlet>< load-on-startup>或@WebServlet(loadOnStartup)值大于0,然后在启动期间使用新的ServletConfig调用其init()方法.这些servlet按照该值指定的相同顺序进行初始化(1为1,2,为2等) .如果为多个servlet指定了相同的值,则每个servlet的加载顺序与web.xml,web-fragment.xml或@WebServlet类加载中的顺序相同.如果没有“load-on-startup”值,只要HTTP请求第一次访问该servlet,就会调用init()方法.

当servlet容器完成所有上述初始化步骤时,将调用ServletContextListener#contextInitialized().

当servlet容器关闭时,它会卸载所有Web应用程序,调用所有初始化的servlet和过滤器的destroy()方法,并且所有ServletContext,Servlet,Filter和Listener实例都会被删除.最后将调用ServletContextListener#contextDestroyed().

HttpServletRequest和HttpServletResponse

servlet容器连接到Web服务器,该服务器侦听特定端口号上的HTTP请求(端口8080通常在开发期间使用,端口80在生产中使用).当客户端(例如,具有Web浏览器的用户或programmatically using URLConnection)发送HTTP请求时,servlet容器会创建新的HttpServletRequestHttpServletResponse对象,并将它们传递给链中任何已定义的Filter,最终传递给Servlet实例.

filters的情况下,调用doFilter()方法.当servlet容器的代码调用chain.doFilter(request,response)时,请求和响应将继续执行下一个过滤器,或者如果没有剩余过滤器则命中servlet.

servlets的情况下,调用service()方法.默认情况下,此方法根据request.getMethod()确定要调用哪个doXxx()方法.如果servlet中没有确定的方法,则在响应中返回HTTP 405错误.

请求对象提供对HTTP请求的所有信息的访问,例如URL,标题,查询字符串和正文.响应对象提供了以您希望的方式控制和发送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,然后选中Net / Network选项卡). 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.
    } 
}

也可以看看:

> What is the difference between JSF, Servlet and JSP?
> Best option for Session management in Java
> Difference between / and /* in servlet mapping url pattern
> doGet and doPost in Servlets
> Servlet seems to handle multiple concurrent browser requests synchronously
> Why Servlets are not thread Safe?

转载注明原文:java – servlet如何工作?实例化,会话,共享变量和多线程 - 代码日志