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


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


ServletContext

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

当servlet容器关闭时,它卸载所有Web应用程序,调用destroy()其全部初始化servlet和过滤器,所有的方法ServletContextServletFilterListener实例丢弃。

如果Servlet的值大于<servlet><load-on-startup>@WebServlet(loadOnStartup)大于0,init()则在启动期间也会使用new调用其方法ServletConfig。这些servlet按照该值指定的相同顺序进行初始化(1 - > 1st,2 - > 2nd等)。如果为多个servlet指定了相同的值,则每个servlet按照它们在类web.xml@WebServlet加载中出现的顺序加载。如果不存在“load-on-startup”值,则init()只要HTTP请求第一次访问该servlet,就会调用该方法。

HttpServletRequest和HttpServletResponse

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

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

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

请求对象提供对HTTP请求的所有信息的访问,例如其标题和正文。响应对象提供了以您希望的方式控制和发送HTTP响应的功能,例如,允许您设置标头和正文(通常使用JSP文件中生成的HTML内容)。提交并完成HTTP响应后,请求和响应对象都将被回收并重新使用。

HttpSession

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

根据HTTP cookie规范(一个合适的Web浏览器和Web服务器必须遵守的合同),Cookie只要cookie有效,客户端(Web浏览器)就需要在标头中的后续请求中发回此cookie。(即唯一ID必须指未到期的会话,域和路径是正确的)。使用浏览器的内置HTTP流量监视器,您可以验证cookie是否有效(在Chrome / Firefox 23+ / IE9 +中按F12,然后检查网络/网络选项卡)。servlet容器将检查Cookie每个传入HTTP请求的标头是否存在带有名称的cookie,JSESSIONID并使用其值(会话ID)HttpSession从服务器的内存中获取关联的。

该HttpSession活直至其没有被用于除在指定的超时值时,在一个设定web.xml。超时值默认为30分钟。因此,当客户端访问Web应用程序的时间超过指定的时间时,servlet容器会破坏会话。即使指定了cookie,每个后续请求也将无法再访问同一个会话; servlet容器将创建一个新会话。

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

简而言之

  • ServletContext,只要Web应用程序生命。它在所有会话中的所有请求之间共享。
  • HttpSession,只要生命在客户端与使用相同的浏览器实例中的Web应用程序进行交互,和会话未在服务器端超时。它在同一会话中的所有请求之间共享。
  • 在HttpServletRequest和HttpServletResponse从servlet接收来自客户端的HTTP请求的时间住,直到完全缓解(网页)已经到来。它不在别处分享。
  • 只要网络应用程序存在Servlet,所有Filter和Listener实例都会存在。它们在所有会话中的所有请求之间共享。
  • 任何attribute被定义的ServletContext,HttpServletRequest并且HttpSession只要有问题的对象的生活会生活。对象本身表示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.
    }
}