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.xml、web-fragment.xml 或 @WebServlet 类加载中出现的顺序相同。如果“启动时加载”值不存在,init() 方法将在 HTTP request 第一次访问该 servlet 时被调用。
当 servlet 容器完成上述所有初始化步骤后,将使用 ServletContextEvent 参数调用 ServletContextListener#contextInitialized(),该参数又包含相关的 ServletContext。这将使开发人员有机会以编程方式注册另一个Servlet、Filter或Listener。
当 servlet 容器关闭时,它会卸载所有 Web 应用程序,调用所有已初始化的 servlet 和过滤器的 destroy() 方法,并且所有通过 ServletContext 注册的 Servlet、Filter 和 Listener 实例都将被丢弃。最后 ServletContextListener#contextDestroyed() 将被调用并且 ServletContext 本身将被丢弃。
HttpServletRequest和HttpServletResponse
servlet 容器附加到 Web 服务器,该服务器在特定端口号(开发期间通常使用端口 8080,生产中通常使用端口 80)上侦听 HTTP 请求。当客户端(例如使用 Web 浏览器的用户或programmatically using URLConnection)发送 HTTP 请求时,servlet 容器会创建新的HttpServletRequest 和HttpServletResponse 对象,并通过链中任何已定义的Filter 传递它们,最终, Servlet 实例。
在filters 的情况下,将调用doFilter() 方法。当 servlet 容器的代码调用 chain.doFilter(request, response) 时,请求和响应继续到下一个过滤器,或者如果没有剩余的过滤器则命中 servlet。
在servlets 的情况下,将调用service() 方法。默认情况下,此方法确定根据 request.getMethod() 调用 doXxx() 方法中的哪一个。如果 servlet 中不存在确定的方法,则在响应中返回 HTTP 405 错误。
请求对象提供对有关 HTTP 请求的所有信息的访问,例如它的 URL、headers、query string 和正文。响应对象提供了以您想要的方式控制和发送 HTTP 响应的能力,例如,允许您设置标头和正文(通常使用从 JSP 文件生成的 HTML 内容)。当提交并完成 HTTP 响应时,请求和响应对象都将被回收并可供重用。
HttpSession
当客户端第一次访问 webapp 和/或第一次通过request.getSession()获取HttpSession时,servlet容器创建一个新的HttpSession对象,生成一个长而唯一的ID(你可以得到session.getId()),并将其存储在服务器的内存中。 servlet 容器还在 HTTP 响应的 Set-Cookie 标头中设置 Cookie,并将 JSESSIONID 作为其名称和唯一会话 ID 作为其值。
根据HTTP cookie specification(任何体面的网络浏览器和网络服务器都必须遵守的合同),客户端(网络浏览器)需要在Cookie标头中的后续请求中发送此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 容器会将 session 丢弃。每个后续请求,即使指定了 cookie,也将无法再访问同一会话; servlet 容器将创建一个新会话。
在客户端,只要浏览器实例在运行,会话 cookie 就会保持活动状态。因此,如果客户端关闭浏览器实例(所有选项卡/窗口),那么会话将在客户端被丢弃。在新的浏览器实例中,与会话关联的 cookie 将不存在,因此将不再发送它。这会导致创建一个全新的HttpSession,并使用一个全新的会话 cookie。
简而言之
-
ServletContext 与网络应用程序一样存在。它是共享的全部要求在全部会议。
-
HttpSession 只要客户端使用相同的浏览器实例与 Web 应用交互,并且服务器端的会话没有超时,就会一直存在。它是共享的全部中的请求相同的会议。
-
HttpServletRequest 和HttpServletResponse 从 servlet 收到来自客户端的 HTTP 请求开始,直到完整的响应(网页)到达。这是不是在别处分享。
- 所有
Servlet、Filter 和Listener 实例只要 Web 应用存在就存在。它们之间共享全部要求在全部会议。
- 在
ServletContext、HttpServletRequest 和HttpSession 中定义的任何attribute 将在所讨论的对象存在时存在。对象本身代表 Bean 管理框架(例如 JSF、CDI、Spring 等)中的“作用域”。这些框架将它们的作用域 bean 存储为其最接近的匹配作用域的 attribute。
线程安全
也就是说,您主要关心的可能是thread safety。您现在应该知道 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.
}
}
也可以看看: