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


1142

假设我有一个Web服务器,其中包含许多Servlet。为了在这些servlet之间传递信息,我正在设置会话和实例变量。

现在,如果有2个或更多用户向该服务器发送请求,那么会话变量将如何处理?
它们对所有用户都是通用的还是对每个用户都是不同的?
如果它们不同,那么服务器如何区分不同的用户?

还有一个类似的问题,如果有n用户正在访问特定的servlet,那么仅当第一个用户第一次访问该servlet时才实例化该servlet,还是单独为所有用户实例化该servlet?
换句话说,实例变量将如何处理?

Answers:


1820

ServletContext

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

当a的Servleta <servlet><load-on-startup>@WebServlet(loadOnStartup)value大于时0,则init()在启动期间还会使用new调用其方法ServletConfig。这些servlet以该值指定的相同顺序进行初始化(1为1st,2为2nd等)。如果多于一个的servlet指定了相同的值,则每个这些小服务程序的如它们出现在加载以相同的顺序web.xmlweb-fragment.xml@WebServlet类加载。如果没有“启动时加载”值,则init()当HTTP请求首次访问该servlet时,将调用该方法。

当Servlet容器完成上述所有初始化步骤后,ServletContextListener#contextInitialized()将调用。

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

HttpServletRequest和HttpServletResponse

Servlet容器连接到Web服务器,该Web服务器在某个端口号上侦听HTTP请求(端口8080通常在开发过程中使用,而端口80在生产中使用)。当客户端(例如,具有Web浏览器的用户,或以编程方式使用的用户URLConnection)发送HTTP请求时,Servlet容器将创建new HttpServletRequestHttpServletResponse对象,并将它们通过Filter链中定义的任何对象以及Servlet实例进行传递。

对于filter,将doFilter()调用该方法。当servlet容器的代码调用时chain.doFilter(request, response),请求和响应将继续到下一个过滤器,如果没有剩余的过滤器,则单击servlet。

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

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

HttpSession

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

根据HTTP cookie规范(任何体面的Web浏览器和Web服务器都必须遵守的合同),Cookie只要cookie有效(也就是说,唯一ID必须指向未过期的会话,并且域和路径正确)。使用浏览器的内置HTTP流量监控器,您可以验证Cookie是否有效(在Chrome / Firefox 23 + / IE9 +中按F12,然后检查“ 网络/网络”标签)。Servlet容器将检查Cookie每个传入HTTP请求的标头中是否存在具有该名称的cookie,JSESSIONID并使用其值(会话ID)HttpSession从服务器的内存中获取关联。

HttpSession活,直到它停留已经空闲(即,在请求未使用)超过规定的超时值<session-timeout>,在设定web.xml。超时值默认为30分钟。因此,当客户端访问Web应用程序的时间不超过指定的时间时,Servlet容器将破坏会话。每个后续请求,即使指定了cookie,也将无法再访问同一会话。Servlet容器将创建一个新会话。

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

简而言之

  • ServletContext生活,只要Web应用程序的生命。它在所有会话的所有请求之间共享。
  • HttpSession生活,只要客户端与同一个浏览器实例中的Web应用程序进行交互,和会话未在服务器端超时。它在同一会话的所有请求之间共享。
  • HttpServletRequestHttpServletResponse现场从servlet接收来自客户端的HTTP请求的时间,直到完全缓解(网页)已经到来。它没有在其他地方共享。
  • 只要Web应用程序存在Servlet,所有FilterListener实例都会存在。它们在所有会话的所有请求之间共享。
  • 任何attribute被定义ServletContextHttpServletRequest并且HttpSession只要将生活中的问题生活中的对象。对象本身代表了诸如JSF,CDI,Spring等之类的bean管理框架中的“作用域”。这些框架将其范围内的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.
    } 
}

也可以看看:


25
因此,当我以某种方式可以找到发送给客户端的JSessionId时,可以窃取他的会话了吗?
Toskan

54
@Toskan:是的。这就是所谓的会话固定黑客。请注意,这并非特定于JSP / Servlet。所有其他通过cookie维护会话的服务器端语言也很敏感,例如带有PHPSESSIDcookie的PHP,带有cookie的ASP.NET ASP.NET_SessionID等。这就是为什么不赞成;jsessionid=xxx像某些JSP / Servlet MVC框架那样自动进行URL重写的原因。只需确保会话ID永远不会在URL中或网页中通过其他方式公开,以使不知情的最终用户将不会受到攻击。
BalusC

11
@Toskan:另外,请确保您的Web应用程序对XSS攻击不敏感。即不要以未转义的形式重新显示任何用户控制的输入。XSS为收集所有最终用户的会话ID敞开了大门。另请参阅XSS的一般概念是什么?
BalusC

2
@BalusC,抱歉我的愚蠢。这意味着所有用户都访问thisIsNOTThreadSafe的相同实例,对吗?
黯然失色

4
当缺少整个servlet本身时,返回@TwoThumbSticks 404。当存在servlet但未实现所需的doXxx()方法时,返回405。
BalusC

428

届会

在此处输入图片说明 在此处输入图片说明

总之:Web服务器发出一个唯一的标识符,以每位游客在他的第一次访问。访客必须带回该ID,以便下次被识别。该标识符还允许服务器将一个会话所拥有的对象与另一个会话所拥有的对象正确地隔离开。

Servlet实例化

如果启动时加载false

在此处输入图片说明 在此处输入图片说明

如果启动时加载true

在此处输入图片说明 在此处输入图片说明

一旦他进入了服务模式并参与了工作,同一个 servlet将处理来自所有其他客户端的请求。

在此处输入图片说明

每个客户端一个实例为什么不是一个好主意?考虑一下:您会为每份订单雇用一个披萨人吗?这样做,您很快就会破产。

不过,它带来的风险很小。记住:这个单身汉将所有订单信息都放在口袋里:因此,如果您对servlet的线程安全性不太谨慎,他可能最终会给某个客户错误的订单。


26
您的照片对我的理解非常好。我有一个问题,当披萨订单太多时,这家披萨餐厅会做什么?只等一个披萨店的人或雇用更多披萨店的人?谢谢 。
zh18 2016年

6
他将通过to many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords '17

3
与披萨送货员不同,Servlet可以同时执行多个送货。他们只需要特别注意写下客户地址,披萨的味道...
bruno

42

Java Servlet中的会话与其他语言(例如PHP)中的会话相同。它是用户唯一的。服务器可以以不同的方式(例如cookie,URL重写等)来跟踪它。此Java文档文章在Java servlet的上下文中对其进行了说明,并指出会话的确切维护方式是服务器设计者的实现细节。该规范仅规定,必须在与服务器的多个连接上将其维护为对用户唯一。从Oracle查阅本文,以获取有关这两个问题的更多信息。

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


这给我提出了另一个问题,因为整个应用程序只有一个servlet上下文,并且我们可以通过该servletcontext访问会话变量,那么会话变量如何对每个用户都是唯一的?谢谢..
Ku Jon

1
您如何从ServletContext访问会话?您不是在指servletContext.setAttribute(),对吗?
马特b 2010年

4
@KuJon每个Web应用程序都有一个ServletContext对象。该对象具有零个,一个或多个会话对象-会话对象的集合。每个会话都由某种标识符字符串标识,如其他答案的卡通插图所示。该标识符通过cookie或URL重写在客户端上进行跟踪。每个会话对象都有其自己的变量。
罗勒·布尔克

33

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

在Servlet的实例化阶段,servlet实例已准备就绪,但由于缺少以下两条信息而无法满足客户端请求:
1:上下文信息
2:初始配置信息

Servlet引擎创建ServletConfig接口对象,将上面缺少的信息封装到其中,通过提供ServletConfig对象引用作为参数,Servlet引擎调用Servlet的init()。一旦init()完全执行,servlet就准备好处理客户请求。

问:在Servlet的生命周期中,实例化和初始化发生了多少次?

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

问)会议概念如何工作?

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

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

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

步骤3:如果未创建ID可用的全新会话对象,则使用会话ID作为关键字,从请求对象中检索会话ID,以在会话集合中进行搜索。

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

注意:

1)当控制权从Servlet代码移到客户端时,请不要忘记会话对象由servlet容器(即servlet引擎)持有。

2)多线程处理留给servlet开发人员来实现,即处理客户端的多个请求而不必担心多线程代码

简写形式:

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


20

会议 -克里斯·汤普森说的。

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


3
正确。附加思想:每个请求都会获得一个新的(或回收的)线程,以在该Servlet实例上运行。每个Servlet都有一个实例,并可能有多个线程(如果有多个并发请求)。
罗勒·布尔克

13

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

Servlet容器可以通过Servlet的服务方法发送并发请求。为了处理请求,Servlet开发人员必须为服务方法中的多线程并发处理做好充分的准备。

尽管不建议这样做,但开发人员的另一种选择是实现SingleThreadModel接口,该接口要求容器保证服务方法中一次仅存在一个请求线程。Servlet容器可以通过序列化Servlet上的请求或维护Servlet实例池来满足此要求。如果servlet是已标记为可分发的Web应用程序的一部分,则容器可以在应用程序分布在的每个JVM中维护一个servlet实例池。

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


2
仅供参考,当前Servlet规范(2015-01)为3.1,由JSR 340定义。
罗勒·布尔克


1
很整齐的答案!@tharindu_DG
汤姆·泰勒

0

从上面的解释中可以清楚地看出,通过实现SingleThreadModel,可以通过servlet容器确保servlet的线程安全性。容器实现可以通过两种方式做到这一点:

1)将请求(排队)序列化到单个实例-这类似于未实现SingleThreadModel的servlet,但要同步service / doXXX方法;要么

2)创建实例池-这是一个更好的选择,并且是在Servlet的启动/初始化工作量/时间与托管Servlet的环境的限制性参数(内存/ CPU时间)之间进行权衡的选择。


-1

。Servlet 不是线程安全的

这允许一次访问多个线程

如果您想使其Servlet作为线程安全。,您可以选择

Implement SingleThreadInterface(i) 这是一个空白接口,没有

方法

或者我们可以使用同步方法

我们可以通过使用同步使整个服务方法成为同步的

方法前面的关键字

例::

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

或者我们可以将代码放入同步块中

例::

Synchronized(Object)

{

----Instructions-----

}

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

已同步

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.