servlets如何工作? 实例化,会话,共享variables和multithreading

假设,我有一个拥有众多servlet的web服务器。 对于在这些servlet之间传递的信息,我设置了会话和实例variables。

现在,如果有2个或更多用户向这个服务器发送请求,那么会话variables会发生什么? 它们对于所有的用户都是共同的吗?或者对于每个用户来说都是不同的。 如果它们不同,那么服务器如何能够区分不同的用户呢?

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

ServletContext中

当servlet容器(如Apache Tomcat )启动时,它将部署并加载所有的Web应用程序。 当一个web应用程序被加载时,servlet容器会创build一次ServletContext并将其保存在服务器的内存中。 Web应用程序的web.xml文件被parsing,find每个<servlet><filter><listener> (或者每个类分别使用@WebListener@WebListener@WebListener注解)实例化一次,并保存在服务器的内存中好。 对于每个实例化的filter,其init()方法都会使用一个新的FilterConfig来调用。

当servlet容器closures时,它卸载所有的Web应用程序,调用所有初始化的servlet和filter的destroy()方法,并且所有的ServletContextServletFilterListener实例都被抛出。

当一个Servlet<servlet><load-on-startup>或者@WebServlet(loadOnStartup)值大于0 ,它的init()方法也会在启动时被一个新的ServletConfig调用。 这些servlet的初始化顺序与该值相同(1 – > 1st,2 – > 2nd等)。 如果为多个servlet指定了相同的值,那么这些servlet中的每一个都按照它们出现在web.xml@WebServlet类加载中的顺序加载。 在“load-on-startup”值不存在的情况下,只要HTTP请求第一次访问该servlet,就会调用init()方法。

HttpServletRequest和HttpServletResponse

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

在filter的情况下,调用doFilter()方法。 当代码调用chain.doFilter(request, response) ,请求和响应继续到下一个filter,或者如果没有剩余的filter,则点击servlet。

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

请求对象提供对所有关于HTTP请求的信息的访问,比如它的头部和主体。 响应对象提供了以您想要的方式控制和发送HTTP响应的function,例如,允许您设置标题和正文(通常使用JSP文件中生成的HTML内容)。 当HTTP响应被提交并完成时,请求和响应对象都被回收并重用。

HttpSession中

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

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

HttpSession一直保持活动状态,直到尚未使用超过web.xml设置<session-timeout>指定的超时值。 超时值默认为30分钟。 所以,当客户端没有访问Web应用的时间超过了指定的时间,servlet容器就会抛出会话。 每一个后续的请求,即使是指定的cookie,都不会再访问同一个会话。 该servlet容器将创build一个新的会话。

在客户端,只要浏览器实例正在运行,会话cookie就保持活动状态。 所以,如果客户端closures浏览器实例(所有的标签/窗口),那么会话在客户端被抛弃。 在新的浏览器实例中,与会话关联的cookie将不存在,因此不会再发送。 这将导致创build一个全新的HTTPSession ,并开始使用全新的会话Cookie。

简而言之

  • 只要Web应用程序存在, ServletContext一直存在。 它在所有会议的所有请求中共享。
  • 只要客户端使用相同的浏览器实例与Web应用程序进行交互, HttpSession存在,并且会话在服务器端没有超时。 它在同一会话中的所有请求之间共享。
  • HttpServletRequestHttpServletResponse从Servlet接收到来自客户端的HTTP请求的时间开始直到完整的响应(网页)到达。 它不在别处共享。
  • 所有的ServletFilterListener实例在web应用程序生存期间一直存在。 他们在所有会议的所有请求之间共享。
  • ServletContextHttpServletRequestHttpSession定义的任何attribute都会在所讨论的对象存在的情况下存在。 对象本身代表了beanpipe理框架(如JSF,CDI,Spring等)中的“范围”。这些框架将其作用域Bean存储为最接近的匹配范围的attribute

线程安全

也就是说,你的主要担心是线程安全 。 您现在应该知道,servlet和filter是在所有请求之间共享的。 这是Java的好处,它是multithreading的,不同的线程(读取:HTTP请求)可以使用相同的实例。 否则,对于每个请求重新创build, init()destroy()它们都会花费太多。

你也应该认识到,你不应该把任何请求或会话范围数据作为一个servlet或filter的实例variables。 它将在其他会议中的所有其他请求之间共享。 这不是线程安全的! 下面的例子说明了这一点:

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

也可以看看:

  • JSF,Servlet和JSP有什么区别?
  • Java中会话pipe理的最佳select
  • servlet映射url模式中的/和/ *之间的区别
  • doGet和doPost在Servlets中
  • Servlet似乎同时处理多个并发的浏览器请求
  • 为什么Servlet不是线程安全的?

会议

在这里输入图像描述在这里输入图像描述

简而言之:Web服务器首次访问时向每位访问者发出唯一的标识符。 访客必须带回该身份证,以便他下次得到认可。 这个标识符还允许服务器正确地将一个会话拥有的对象与另一个会话的对象分开。

Servlet实例化

如果load-on-startupfalse

在这里输入图像描述在这里输入图像描述

如果load-on-startup

在这里输入图像描述在这里输入图像描述

一旦他处于服务模式和沟槽中, 相同的 servlet将处理来自所有其他客户端的请求。

在这里输入图像描述

为什么不是每个客户端都有一个实例的好主意? 想想这个:你会为每一个订单雇佣一个比萨饼人吗? 这样做,你会很快失业。

它虽然有一个小的风险。 请记住:这个单独的人把所有的订单信息放在口袋里,所以如果你对Servlet的线程安全性不是很谨慎的话,他最终可能会把错误的订单交给某个客户。

Java servlets中的会话与其他语言(如PHP)中的会话相同。 这对用户来说是独一无二的。 服务器可以用不同的方式跟踪它,如cookie,url重写等。这个Java文档文章在Java servlet的上下文中解释了它,并且指出确切地说session是如何被维护的,是服务器devise者的实现细节。 该规范仅规定,通过与服务器的多个连接,它必须被维护为唯一的。 有关这两个问题的更多信息,请参阅Oracle的这篇文章 。

编辑这里有一个很好的教程,介绍如何使用servlet中的会话。 下面是Sun有关Java Servlets的一章,它们是什么以及如何使用它们。 在这两篇文章之间,你应该能够回答你所有的问题。

当servlet容器(如Apache Tomcat)启动时,如果出现任何错误或在容器端控制台出现错误,它将从web.xml文件(每个应用程序只有一个)读取,否则将使用web部署和加载所有的Web应用程序.xml(因此将其命名为部署描述符)。

在servlet的实例化阶段,servletInstance已经准备好,但是它不能提供客户请求,因为它缺less两条信息:
1:上下文信息
2:初始configuration信息

Servlet引擎创buildservletConfig接口对象,封装上述缺失信息servlet引擎通过将servletConfig对象引用作为参数来调用servlet的init()。 一旦init()完成执行,servlet就准备好服务于客户端请求。

Q)在servlet的生命周期中,实例化和初始化发生了多less次?

A)只有一次(为每个客户端请求创build一个新的线程)只有一个servlet实例服务于任何数量的客户端请求,即服务一个客户端请求服务器之后不会死亡。 它等待其他客户端的请求,即什么CGI(为每个客户端请求一个新的进程创build)限制是通过servlet(内部servlet引擎创build线程)克服的。

问)会议概念如何工作?

A)每当getSession()在HttpServletRequest对象上被调用时

步骤1 :请求对象对传入的会话ID进行evalauated。

步骤2 :如果ID不可用,则创build一个全新的HttpSession对象,并生成相应的会话ID(即HashTable)会话ID存储在httpservlet响应对象中,HttpSession对象的引用返回给servlet(doGet / doPost)。

步骤3 :如果没有创buildID可用品牌新会话对象,则从请求对象中拾取会话ID。使用会话ID作为关键字在会话集合中进行search。

一旦search成功,会话ID被存储到HttpServletResponse中,而现有的会话对象引用被返回给UserDefineservlet的doGet()或doPost()。

注意:

1)当控制离开servlet代码到客户端不要忘记,会话对象正在由servletcontainer举行,即servletengine

2)multithreading留给servlet开发人员来实现,即处理客户端的多重请求,不用担心multithreading代码

短暂的forms:

servlet在应用程序启动时(在servlet容器上部署)或第一次被访问(取决于启动时加载设置)时创build,当servlet实例化时,servlet的init()方法被调用那么servlet(它的唯一实例)处理所有的请求(由多个线程调用它的service()方法)。 这就是为什么不build议在其中进行任何同步,并且应用程序未部署(servlet容器停止)时应该避免servlet的实例variables,调用destroy()方法。

会议 – Chris Thompson说的。

实例化 – 当容器接收到映射到servlet的第一个请求时, 实例化一个servlet(除非servlet被configuration为在web.xml使用<load-on-startup>元素<load-on-startup> )。 同一个实例用于为后续请求提供服务。

Servlet规范JSR-315明确定义了服务(和doGet,doPost,doPut等)方法中的Web容器行为(2.3.3.1multithreading问题,第9页):

一个servlet容器可以通过servlet的服务方法发送并发请求。 为了处理这些请求,Servlet开发者必须在服务方法中为multithreading的并行处理做好充分的准备。

虽然不推荐,但开发人员的替代方法是实现SingleThreadModel接口,该接口要求容器保证在service方法中一次只有一个请求线程。 一个servlet容器可以通过在一个servlet上序列化请求或维护一个servlet实例池来满足这个要求。 如果servlet是标记为可分发的Web应用程序的一部分,那么容器可以在应用程序分布的每个JVM中维护一个servlet实例池。

对于没有实现SingleThreadModel接口的Servlet,如果已经使用synchronized关键字定义了服务方法(或者派发到HttpServlet抽象类的服务方法的doGet或doPost等方法),则Servlet容器不能使用实例池方法,但是必须通过它序列化请求。 强烈build议开发人员不要在这种情况下同步服务方法(或派发给它的方法),因为这会对性能产生不利影响