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

现在,如果两个或更多用户向该服务器发送请求,那么会话变量会发生什么变化

还有一个类似的问题,如果有n个用户访问一个特定的servlet,那么这个servlet只会在第一个用户第一次访问它时被实例化,还是会分别为所有用户实例化

推荐答案

ServletContext

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

Servlet<servlet><load-on-startup>@WebServlet(loadOnStartup)值大于0时,它的init()方法也会在启动期间使用新的ServletConfig参数调用,该参数又包含所涉及的ServletContext.这些servlet按照该值指定的相同顺序初始化(1是第一个,2是第二个,等等).如果为多个servlet指定了相同的值,那么每个servlet的加载顺序与web.xmlweb-fragment.xml@WebServlet类加载中的顺序相同.如果没有"启动时加载"值,那么每当HTTP request第一次点击该servlet时,就会调用init()方法.

当servlet容器完成上述所有初始化步骤后,将使用ServletContextEvent参数调用ServletContextListener#contextInitialized(),该参数又包含所涉及的ServletContext.这将使开发人员有机会以编程方式注册另一个ServletFilterListener.

当servlet容器关闭时,它卸载所有web应用程序,调用其所有初始化servlet和过滤器的destroy()方法,通过ServletContext注册的所有ServletFilterListener实例都被丢弃.最后,ServletContextListener#contextDestroyed()将被调用,而ServletContext本身将被丢弃.

HttpServletRequest and HttpServletResponse

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

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

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

Request对象提供对有关HTTP请求的所有信息的访问,例如其URLheadersquery string和正文.Response对象提供了以您想要的方式控制和发送HTTP响应的能力,例如,允许您设置头和正文(通常带有从JSP文件生成的HTML内容).当HTTP响应提交并完成时,请求和响应对象都将被回收并可供重用.

HttpSession

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

根据HTTP cookie specification(任何像样的web浏览器和web服务器都必须遵守的合同),只要cookie有效(即,唯一ID必须引用未过期的会话并且域和路径正确),客户端(web浏览器)就需要在Cookie报头中的后续请求中发回此cookie.使用浏览器内置的HTTP流量监控器,您可以验证cookie是否有效(在Chrome/Firefox 23+/IE9+中按F12,然后判断Net/Network选项卡).Servlet容器将判断每个传入HTTP请求的Cookie头是否存在名为JSESSIONID的cookie,并使用其值(会话ID)从服务器内存中获取关联的HttpSession.

HttpSession将保持活动状态,直到其空闲(即未在请求中使用)超过<session-timeout>中指定的超时值(web.xml中的设置).超时值默认为30分钟.因此,当客户机访问web应用的时间超过指定的时间时,servlet容器会丢弃session个应用程序.每个后续请求,即使指定了cookie,也将无法再访问同一会话;servlet容器将创建一个新会话.

在客户端,只要浏览器实例在运行,会话cookie就会保持活动状态.因此,如果客户端关闭浏览器实例(所有选项卡/窗口),则会话在客户端被 destruct .在新的浏览器实例中,与会话关联的cookie将不存在,因此它将不再被发送.这将创建一个全新的HttpSession,并使用一个全新的会话cookie.

简而言之,简而言之

  • ServletContext人的生命周期 与web应用的生命周期 一样长.它在all个会话中的all个请求中共享.
  • 只要客户端使用相同的浏览器实例与Web应用程序交互,并且服务器端的会话没有超时,HttpSession就会一直存在.它在same会话中的all个请求之间共享.
  • HttpServletRequestHttpServletResponse从servlet从客户端接收HTTP请求开始,一直持续到完整响应(网页)到达为止.它在其他地方共享.
  • 所有Servlet个、Filter个和Listener个实例的生命周期 与web应用的生命周期 一样长.它们在all个会话的all个请求中共享.
  • ServletContextHttpServletRequestHttpSession中定义的任何attribute都将与所述对象的生命周期 一样长.对象本身表示bean管理框架(如JSF、CDI、Spring等)中的"作用域".这些框架将其作用域bean存储为其最接近匹配作用域的attribute.

线程安全

也就是说,你主要关心的可能是thread safety.您现在应该知道,servlet和过滤器是在所有请求之间共享的.这就是Java的优点,它是多线程的,不同的线程(读:HTTP请求)可以使用同一个实例.否则,重新创建它们的成本太高,每一个请求都有init()个和destroy()个.

您还应该意识到,应该将任何请求或会话范围的数据分配为servlet或过滤器的instance变量.它将在其他会话中的所有其他请求中共享.这是not线程安全!下面的例子说明了这一点:

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

另见:

Java相关问答推荐

OpenRewriter-如何替换连锁/流畅方法调用中的方法?

Java SSLocket查明客户端是否发送了证书

更新我们的一个文物后出现了严重的符号引用错误

OpenJDK、4K显示和文本质量

空手道比赛条件

Cucumber TestNG Assert失败,出现java. lang. Numbercycle异常

如何使用Java API在Oracle ODI中运行模拟?

在Java中测试DAO方法:假实现与内存数据库

Java函数式编程中的双值单值映射

CAMEL 4中的SAXParseException

滚动视图&不能在alert 对话框中工作(&Q;&Q;)

在Java Swing Paint应用程序中捕获快速鼠标移动时遇到困难

Java-动态绑定-问题-了解

Sack()步骤中的合并运算符未按预期工作

是否为计划任务补偿系统睡眠?

有没有办法知道在合并中执行了什么操作?

在实例化中指定泛型类型与不指定泛型类型之间的区别

用于Java的Visual Studio代码完成不起作用

java.lang.ClassCastException:com.google.firebase.FirebaseException无法转换为com.google.fire base.auth.FirebaseAuthException

javax.crypto-密码对象-提供者服务是如何工作的?