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

Ku *_*Jon 1105 java multithreading servlets session-variables instance-variables

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

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

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

Bal*_*usC 1778

ServletContext中

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

当servlet容器关闭时,它卸载所有Web应用程序,调用FilterConfig其全部初始化servlet和过滤器,所有的方法Servlet,<servlet><load-on-startup>,@WebServlet(loadOnStartup)0实例丢弃.

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

HttpServletRequest和HttpServletResponse

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

过滤器的情况下,HttpServletRequest调用该方法.当servlet容器的代码调用时HttpServletResponse,请求和响应继续到下一个过滤器,或者如果没有剩余的过滤器则命中servlet.

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

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

HttpSession中

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

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

所述Cookie保持活着,直到它已经空闲(即,在请求未使用)超过指定的超时值更Cookie,在设定JSESSIONID.超时值默认为30分钟.因此,当客户端访问Web应用程序的时间超过指定的时间时,servlet容器会破坏会话.即使指定了cookie,每个后续请求也将无法再访问同一个会话; servlet容器将创建一个新会话.

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

简而言之

  • HttpSession生活,只要Web应用程序生命.它在所有会话中的所有请求之间共享.
  • <session-timeout>,只要生命在客户端与使用相同的浏览器实例中的Web应用程序进行交互,和会话未在服务器端超时.它在同一会话中的所有请求之间共享.
  • web.xmlHttpSession从servlet接收来自客户端的HTTP请求的时间住,直到完全缓解(网页)已经到来.它不在别处分享.
  • 只要Web应用程序存在ServletContext,所有HttpSessionHttpServletRequest实例都会存在.它们在所有会话中的所有请求之间共享.
  • 任何HttpServletResponse被定义的Servlet,Filter并且Listener只要有问题的对象的生活会生活.对象本身代表bean管理框架中的"范围",例如JSF,CDI,Spring等.这些框架将其作用域bean存储attribute为其最接近的匹配范围.

线程安全

也就是说,您主要关心的可能是线程安全.您现在应该知道servlet和过滤器在所有请求之间共享.这是关于Java的好东西,它是多线程的,不同的线程(读取:HTTP请求)可以使用相同的实例.否则,重新创建它会太昂贵,ServletContext并且HttpServletRequest它们对于每个请求都是如此.

您还应该意识到,您永远不应将任何请求或会话范围数据分配为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.
    } 
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • @Toskan:那是对的.它被称为[会话固定黑客](http://en.wikipedia.org/wiki/Session_fixation).请注意,这不是JSP/Servlet特有的.它通过一个cookie保持会话的所有其他服务器端语言是敏感的,以及像PHP与`PHPSESSID`饼干,ASP.NET使用`ASP.NET_SessionID`饼干,等等.这也是为什么URL重写与`; jsessionid = xxx`一样,因为一些JSP/Servlet MVC框架会自动执行.只需确保会话ID永远不会在URL中或通过网页中的其他方式公开,以便不会攻击不知不觉的最终用户. (51认同)
  • 所以,当我以某种方式找到发送给客户端的JSessionId时,我可以窃取他的会话吗? (22认同)
  • @Toskan:另外,请确保您的webapp对XSS攻击不敏感.即不要以非转义形式重新显示任何用户控制的输入.XSS打开了收集所有最终用户的会话ID的方法.另请参见[XSS背后的一般概念是什么?](http://stackoverflow.com/questions/2233015/what-is-the-general-concept-behind-xss/2233110#2233110) (10认同)
  • 当整个servlet本身不存在时,返回@TwoThumbSticks 404.当servlet存在但是未实现所需的doXxx()方法时返回405. (3认同)
  • @BalusC,抱歉我的愚蠢。这意味着所有用户都访问 thisIsNOTThreadSafe 的同一个实例,对吗? (2认同)

Jop*_*ops 425

会议

在此输入图像描述 在此输入图像描述

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

Servlet实例化

如果load-on-startupfalse:

在此输入图像描述 在此输入图像描述

如果load-on-startuptrue:

在此输入图像描述 在此输入图像描述

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

在此输入图像描述

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

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

  • 你的照片非常适合我的理解.我有一个问题,当披萨订单太多时,这家披萨餐厅会做什么,只等一个披萨店或者雇佣更多披萨店的人?谢谢 . (26认同)
  • 他将在此时向许多请求返回一条消息.稍后再试一次 (6认同)
  • 与披萨送货员不同,Servlet可以同时执行多个送货。他们只需要特别注意写下客户地址,披萨的味道... (3认同)

Chr*_*son 42

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

编辑这里有一个很好的教程,介绍如何在servlet中使用session.而这里是来自Sun关于Java Servlet的,它们是什么,以及如何使用它们的一章.在这两篇文章之间,您应该能够回答所有问题.

  • @KuJon每个Web应用程序都有一个`ServletContext`对象.该对象具有零个,一个或多个会话对象 - 会话对象的集合.每个会话都由某种标识符字符串标识,如其他答案中的漫画所示.通过cookie或URL重写在客户端上跟踪该标识符.每个会话对象都有自己的变量. (4认同)

Aja*_*kur 33

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

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

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

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

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

问)会话概念如何运作?

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

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

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

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

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

注意:

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

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

简短形式:

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


Lau*_*nen 20

会议 - 克里斯汤普森所说的话.

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

  • 正确.额外的想法:每个请求获得一个新的(或循环的)线程在该单个Servlet实例上运行.每个Servlet都有一个实例,可能还有很多线程(如果同时有多个请求). (3认同)

tha*_*_DG 13

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容器无法使用实例池方法,但必须通过它序列化请求.强烈建议开发人员在这些情况下不要同步服务方法(或发送给它的方法),因为它会对性能产生不利影响

  • 仅供参考,目前的Servlet规范(2015-01)为3.1,由[JSR 340](https://jcp.org/aboutJava/communityprocess/final/jsr340/)定义. (2认同)

归档时间:

查看次数:

284587 次

最近记录:

6 年,9 月 前