首页 文章

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

提问于
浏览
1026

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

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

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

7 回答

  • 40

    当servlet容器(如Apache Tomcat)启动时,如果出现任何问题或者在容器侧控制台上显示错使用web.xml的应用程序(将其命名为部署描述符) .

    在servlet的实例化阶段,servlet实例已准备就绪,但它无法为客户端请求提供服务,因为它缺少两条信息:
    1:上下文信息
    2:初始配置信息

    Servlet引擎创建servletConfig接口对象,将上面缺少的信息封装到servlet引擎中,通过提供servletConfig对象引用作为参数来调用servlet的init() . 一旦init()完全执行,servlet就可以为客户端请求提供服务 .

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

    A)只有一次(对于每个客户端请求创建一个新线程),只有一个servlet实例服务于任意数量的客户端请求,即在服务一个客户端请求服务器之后不会死亡 . 它等待其他客户端请求,即使用servlet(内部servlet引擎创建线程)克服了CGI(为每个客户端请求创建新进程)的限制 .

    Q)会话概念如何运作?

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

    Step 1 :评估请求对象的传入会话ID .

    Step 2 :如果ID不可用,则创建一个全新的HttpSession对象并生成其对应的会话ID(即HashTable)会话ID存储到httpservlet响应对象中,并将HttpSession对象的引用返回给servlet(doGet / doPost) .

    Step 3 :如果未创建ID可用的全新会话对象,则从请求对象中选取会话ID,并使用会话ID作为密钥在会话集合中进行搜索 .

    一旦搜索成功,会话ID就会存储到HttpServletResponse中,现有的会话对象引用将返回给UserDefineservlet的doGet()或doPost() .

    注意:

    1)当控制从servlet代码离开到客户端时,不要忘记servlet容器正在保存会话对象,即servlet引擎

    2)多线程留给servlet开发人员实现ie . ,处理客户端的多个请求无需担心多线程代码

    Inshort表格:

    当应用程序启动时(它部署在servlet容器上)或首次访问时(取决于load-on-startup设置),当servlet被实例化时,会创建一个servlet,调用servlet的init()方法然后servlet(它的唯一实例)处理所有请求(由多个线程调用其service()方法) . 这就是为什么不建议在其中进行任何同步,并且在取消部署应用程序(servlet容器停止)时应该避免servlet的实例变量,调用destroy()方法 .

  • 408

    ServletContext

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

    当servlet容器关闭时,它会卸载所有Web应用程序,调用其所有已初始化的servlet和过滤器的 destroy() 方法,并且所有 ServletContextServletFilterListener 实例都会被删除 .

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

    HttpServletRequest和HttpServletResponse

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

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

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

    请求对象提供对HTTP请求的所有信息的访问,例如其 Headers 和正文 . 响应对象提供了以您希望的方式控制和发送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服务器必须遵守的 Contract ),只要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就会保持活动状态 . 因此,如果客户端关闭浏览器实例(所有选项卡/窗口),则会话客户端上的会话被删除,因此将不再发送 . 这会导致创建一个全新的 HTTPSession ,并开始使用全新的会话cookie .

    简而言之

    • 只要网络应用程序存在,_333180就会存在 . 它在所有会话中的所有请求之间共享 .

    • 只要客户端使用相同的浏览器实例与Web应用程序交互, HttpSession 就会存在,并且会话在服务器端没有超时 . 它在同一会话中的所有请求之间共享 .

    • HttpServletRequestHttpServletResponse 从servlet收到来自客户端的HTTP请求开始,直到完整响应(网页)到达 . 它不在别处分享 .

    • 所有 ServletFilterListener 实例只要Web应用程序存在就会生效 . 它们在所有会话中的所有请求之间共享 .

    • 只要有问题的对象存在, ServletContextHttpServletRequestHttpSession 中定义的任何 attribute 都将存在 . 对象本身表示bean管理框架中的"scope",例如JSF,CDI,Spring等 . 这些框架将其作用域bean存储为其最接近匹配范围的 attribute .

    螺纹安全

    也就是说,您主要担心的可能是线程安全 . 您现在应该知道servlet和过滤器在所有请求之间共享 . 's the nice thing of Java, it'的多线程和不同线程(读取:HTTP请求)可以使用相同的实例 . 否则,重新创建它会太昂贵, init()destroy() 他们为每一个请求 .

    您还应该意识到,您应该将任何请求或会话范围数据分配为servlet或过滤器的实例变量 . 它将在其他会话中的所有其他请求之间共享 . 这是 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.
        } 
    }
    

    另见:

  • 1665

    No. Servlets是 not Thread safe

    允许一次访问多个线程

    if u want to make it Servlet as Thread safe ., U can go for

    Implement SingleThreadInterface(i) 这是一个空白界面没有

    方法

    或者我们可以去同步方法

    我们可以使用synchronized来使整个服务方法同步

    方法面前的keword

    Example::

    public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException
    

    或者我们可以在Synchronized块中放置代码块

    Example::

    Synchronized(Object)
    
    {
    
    ----Instructions-----
    
    }
    

    我觉得Synchronized块比制作整个方法更好

    同步

  • 13

    Sessions - 克里斯汤普森说的话 .

    Instantiation - 当容器接收到映射到servlet的第一个请求时,实例化servlet(除非servlet配置为在 web.xml 中使用 <load-on-startup> 元素启动时加载) . 相同的实例用于服务后续请求 .

  • 0

    会话

    enter image description here

    enter image description here

    简而言之:Web服务器在首次访问时向每个访问者发出唯一标识符 . 访问者必须带回该ID,以便下次被识别 . 此标识符还允许服务器正确地将一个会话拥有的对象与另一个会话拥有的对象隔离 .

    Servlet实例化

    如果 load-on-startupfalse

    enter image description here

    enter image description here

    如果 load-on-startuptrue

    enter image description here

    enter image description here

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

    enter image description here

    为什么每个客户端都有一个实例不是一个好主意?想一想:你会为每一个订单雇用一个披萨店吗?这样做,你很快就会破产 .

    但它带来的风险很小 . 记住:这个单身男人把所有的订单信息放在口袋里:所以如果你对thread safety on servlets不小心,他最终可能会给某个客户提供错误的订单 .

  • 20

    Java servlet中的会话与其他语言(如PHP)中的会话相同 . 它对用户来说是独一无二的 . 服务器可以用不同的方式跟踪它,例如cookie,url重写等 . 这篇文章在Java servlet的上下文中对它进行了解释,并指出如何维护会话是一个留给服务器设计者的实现细节 . 该规范仅规定必须通过与服务器的多个连接将其维护为对用户唯一 . 有关您的两个问题的详细信息,请查看this article from Oracle .

    Edit 关于如何在servlet内部使用会话,有一个很好的教程here . here是Sun关于Java Servlets的章节,它们是什么以及如何使用它们 . 在这两篇文章之间,您应该能够回答所有问题 .

  • 31

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

    servlet容器可以通过servlet的服务方法发送并发请求 . 为了处理请求,Servlet Developer必须为服务方法中的多个线程的并发处理提供充分的条件 . 虽然不推荐使用,但Developer的另一个选择是实现SingleThreadModel接口,该接口要求容器保证服务方法中一次只有一个请求线程 . servlet容器可以通过序列化servlet上的请求或维护servlet实例池来满足此要求 . 如果servlet是已标记为distributable的Web应用程序的一部分,则容器可以在应用程序分布的每个JVM中维护一个servlet实例池 . 对于未实现SingleThreadModel接口的servlet,如果已使用synchronized关键字定义了服务方法(或调度到HttpServlet抽象类的服务方法的doGet或doPost等方法),则servlet容器无法使用实例池方法,但必须通过它序列化请求 . 强烈建议开发人员在这些情况下不要同步服务方法(或派遣给它的方法),因为它会对性能产生不利影响

相关问题