假设我有一个网络服务器,其中包含许多 servlet。对于在这些 servlet 之间传递的信息,我设置会话和实例变量。
现在,如果 2 个或更多用户向该服务器发送请求,那么会话变量会发生什么?
它们对于所有用户来说都是通用的还是对于每个用户来说都是不同的?
如果它们不同,那么服务器如何区分不同的用户?
还有一个类似的问题,如果有 n 个用户访问特定的 servlet,那么该 servlet 仅在第一个用户第一次访问它时才会实例化,还是会为所有用户单独实例化?
换句话说,实例变量会发生什么?
Suppose, I have a webserver which holds numerous servlets. For information passing among those servlets I am setting session and instance variables.
Now, if 2 or more users send request to this server then what happens to the session variables?
Will they all be common for all the users or they will be different for each user?
If they are different, then how was the server able to differentiate between different users?
One more similar question, if there are n
users accessing a particular servlet, then this servlet gets instantiated only the first time the first user accessed it or does it get instantiated for all the users separately?
In other words, what happens to the instance variables?
发布评论
评论(8)
ServletContext
当servlet容器(如Apache Tomcat)启动时,它将部署并加载它的所有网络应用程序。加载 Web 应用程序时,Servlet 容器会创建
的实例ServletContext
一次并将其保存在服务器的内存中。解析 Web 应用程序的web.xml
和所有包含的web-fragment.xml
文件,并且每个
、找到了
和
(或者用@WebServlet
、@WebFilter
和注释的每个类) @WebListener
)将被实例化一次,并保存在服务器的内存中,通过 ServletContext 注册。对于每个实例化的过滤器,都会使用 init() 方法rel="noreferrer">FilterConfig
作为参数,该参数又包含所涉及的ServletContext
。当
Servlet
的
或@WebServlet(loadOnStartup)
值为0 时
或更大,那么它的init()
方法也会在启动过程中被调用。这些 servlet 按照该值指定的相同顺序进行初始化。如果为多个 servlet 指定了相同的值,那么每个 servlet 都会按照它们在web.xml
、web-fragment.xml
中出现的顺序加载> 或@WebServlet
类加载。如果“load-on-startup”值不存在或为负值,则每当 init() 方法/Hypertext_Transfer_Protocol#Request_message" rel="noreferrer">HTTP 请求第一次到达该 servlet。有两种init()
方法,一种采用ServletConfig
作为参数,其中包含所涉及的ServletContext
,另一个参数除了ServletContext
之外不接受任何参数可通过继承的 getServletContext() 方法获得。当 Servlet 容器完成上述所有初始化步骤后,
ServletContextListener#contextInitialized()
将使用ServletContextEvent
参数,该参数又包含所涉及的ServletContext
。这将使开发人员有机会以编程方式注册另一个Servlet
、Filter
或Listener
。当 servlet 容器关闭时,它会卸载所有 Web 应用程序,调用所有初始化的 servlet 和过滤器以及所有
Servlet
、Filter< 的
destroy()
方法。通过ServletContext
注册的 /code> 和Listener
实例将被丢弃。最后ServletContextListener#contextDestroyed ()
将被调用,并且ServletContext
本身将被丢弃。HttpServletRequest
和HttpServletResponse
servlet 容器附加到一个 Web 服务器,该服务器在某个端口号上侦听 HTTP 请求(端口 8080 通常在开发期间使用,端口 80 在生产中使用) 。当客户端(例如使用 Web 浏览器的用户,或 以编程方式使用
URLConnection
)发送 HTTP 请求,servlet 容器创建HttpServletRequest
和HttpServletResponse
并将它们传递给链中任何已定义的Filter
,最终通过Servlet
实例。对于过滤器,将调用
doFilter()
方法。当 servlet 容器的代码调用chain.doFilter(request, response)
时,请求和响应将继续到下一个过滤器,或者如果没有剩余的过滤器,则命中 servlet。对于 servlet,将调用
service()
方法。默认情况下,此方法根据request.getMethod()
确定要调用哪一个doXxx()
方法。如果 servlet 中不存在所确定的方法,则响应中将返回 HTTP 405 错误。request 对象提供对有关 HTTP 请求的所有信息的访问,例如其 URL、< a href="https://en.wikipedia.org/wiki/List_of_HTTP_header_fields" rel="noreferrer">标头, 查询字符串和正文。响应对象提供了按照您想要的方式控制和发送 HTTP 响应的能力,例如,允许您设置标头和正文(通常使用从 JSP 文件生成的 HTML 内容)。当 HTTP 响应提交并完成时,请求和响应对象都将被回收并可供重用。
HttpSession
当客户端第一次访问 Web 应用程序和/或
HttpSession
第一次通过request.getSession()
获取,servlet 容器创建一个新的HttpSession< /code> 对象,生成一个长且唯一的 ID(可以通过
session.getId()
获取),并将其存储在服务器内存中。 Servlet 容器还设置Cookie
< /a> 位于 HTTP 响应的Set-Cookie
标头中,以JSESSIONID
作为名称,以唯一会话 ID 作为其值。根据 HTTP cookie 规范(任何合适的 Web 浏览器和 Web 服务器都必须遵守的合同) ),客户端(Web 浏览器)需要在后续请求中发送此 cookie只要 cookie 有效,
Cookie
标头就有效(即唯一 ID 必须引用未过期的会话,并且域和路径正确)。使用浏览器内置的 HTTP 流量监视器,您可以验证 cookie 是否有效(在 Chrome / Edge / Firefox 23+ / IE9+ 中按 F12,然后检查网络/网络选项卡)。 servlet 容器将检查每个传入 HTTP 请求的Cookie
标头是否存在名为JSESSIONID
的 cookie,并使用其值(会话 ID)获取关联的来自服务器内存的HttpSession
。HttpSession
保持活动状态,直到其空闲(即未在请求中使用)时间超过
中指定的超时值(
中的设置)代码> web.xml 。默认超时值取决于 servlet 容器,通常为 30 分钟。因此,当客户端访问 Web 应用程序的时间超过指定的时间时,Servlet 容器会丢弃 会话。每个后续请求,即使指定了 cookie,也将无法再访问同一会话; servlet 容器将创建一个新会话。在客户端,只要浏览器实例正在运行(正常情况下),会话 cookie 就会一直保留。除非浏览器配置为恢复最后一个浏览器会话,当客户端关闭浏览器实例(所有选项卡/窗口)时,会话在客户端丢失。在新的浏览器实例中,与会话关联的 cookie 将不存在,因此将不再发送。这会导致创建一个全新的
HttpSession
,并使用一个全新的会话cookie。简而言之,
ServletContext
与 Web 应用程序一样存在。它在所有会话中的所有请求之间共享。HttpSession
就会一直存在。它在同一会话中的所有请求之间共享。HttpServletRequest
和HttpServletResponse
从 servlet 收到来自客户端的 HTTP 请求时起一直存在,直到完整的响应(网页)到达为止。它未在其他地方共享。Servlet
、Filter
和Listener
实例都会存在。它们在所有会话中的所有请求之间共享。ServletContext
、HttpServletRequest
和HttpSession
中定义的任何attribute
都将随着相关对象的存在而存在。对象本身代表 Bean 管理框架(如 JSF、CDI、Spring 等)中的“范围”。这些框架将其范围内的 Bean 存储为其最接近匹配范围的属性
。线程安全
也就是说,您主要关心的可能是线程安全。您现在应该知道 servlet 和过滤器在所有请求之间共享。这就是 Java 的优点,它是多线程的,不同的线程(阅读:HTTP 请求)可以使用同一个实例。否则,为每个请求重新创建、
init()
和destroy()
它们的成本会太高。您还应该认识到,您不应该永远将任何请求或会话范围的数据分配为servlet 或过滤器的实例变量。它将在其他会话中的所有其他请求之间共享。这不是线程安全的!下面的示例说明了这一点:
另请参阅:
ServletContext
When the servlet container (like Apache Tomcat) starts up, it will deploy and load all its web applications. When a web application is loaded, the servlet container creates an instance of
ServletContext
once and keeps it in the server's memory. The web app'sweb.xml
and all of includedweb-fragment.xml
files is parsed, and each<servlet>
,<filter>
and<listener>
found (or each class annotated with@WebServlet
,@WebFilter
and@WebListener
respectively) will be instantiated once and be kept in the server's memory as well, registred via theServletContext
. For each instantiated filter, itsinit()
method is invoked with an instance ofFilterConfig
as argument which in turn contains the involvedServletContext
.When a
Servlet
has a<servlet><load-on-startup>
or@WebServlet(loadOnStartup)
value of0
or greater, then itsinit()
method is also invoked during startup. Those servlets are initialized in the same order specified by that value. If the same value is specified for more than one servlet, then each of those servlets is loaded in the same order as they appear in theweb.xml
,web-fragment.xml
, or@WebServlet
classloading. In the event the "load-on-startup" value is absent or negative, theinit()
method will be invoked whenever the HTTP request hits that servlet for the very first time. There are twoinit()
methods, one taking an instance ofServletConfig
as argument which in turn contains the involvedServletContext
, and another which does not take any arguments but theServletContext
is available by inheritedgetServletContext()
method.When the servlet container is finished with all of the above described initialization steps, then the
ServletContextListener#contextInitialized()
will be invoked with aServletContextEvent
argument which in turn contains the involvedServletContext
. This will allow the developer the opportunity to programmatically register yet anotherServlet
,Filter
orListener
.When the servlet container shuts down, it unloads all web applications, invokes the
destroy()
method of all its initialized servlets and filters, and allServlet
,Filter
andListener
instances registered via theServletContext
are trashed. Finally theServletContextListener#contextDestroyed()
will be invoked and theServletContext
itself will be trashed.HttpServletRequest
andHttpServletResponse
The servlet container is attached to a web server that listens for HTTP requests on a certain port number (port 8080 is usually used during development and port 80 in production). When a client (e.g. user with a web browser, or programmatically using
URLConnection
) sends an HTTP request, the servlet container creates new instances ofHttpServletRequest
andHttpServletResponse
and passes them through any definedFilter
in the chain and, eventually, theServlet
instance.In the case of filters, the
doFilter()
method is invoked. When the servlet container's code callschain.doFilter(request, response)
, the request and response continue on to the next filter, or hit the servlet if there are no remaining filters.In the case of servlets, the
service()
method is invoked. By default, this method determines which one of thedoXxx()
methods to invoke based off ofrequest.getMethod()
. If the determined method is absent from the servlet, then an HTTP 405 error is returned in the response.The request object provides access to all of the information about the HTTP request, such as its URL, headers, query string and body. The response object provides the ability to control and send the HTTP response the way you want by, for instance, allowing you to set the headers and the body (usually with generated HTML content from a JSP file). When the HTTP response is committed and finished, both the request and response objects are recycled and made available for reuse.
HttpSession
When a client visits the webapp for the first time and/or the
HttpSession
is obtained for the first time viarequest.getSession()
, the servlet container creates a newHttpSession
object, generates a long and unique ID (which you can get bysession.getId()
), and stores it in the server's memory. The servlet container also sets aCookie
in theSet-Cookie
header of the HTTP response withJSESSIONID
as its name and the unique session ID as its value.As per the HTTP cookie specification (a contract any decent web browser and web server must adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the
Cookie
header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Edge / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check theCookie
header of every incoming HTTP request for the presence of the cookie with the nameJSESSIONID
and use its value (the session ID) to get the associatedHttpSession
from server's memory.The
HttpSession
stays alive until it has been idle (i.e. not used in a request) for more than the timeout value specified in<session-timeout>
, a setting inweb.xml
. The default timeout value depends on the servlet container and is usually 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.On the client side, the session cookie remains as long as the browser instance is running (normally). Unless the browser is configured to restore the last browser session, when the client closes the browser instance (all tabs/windows), the session is lost on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new
HttpSession
to be created, with an entirely new session cookie being used.In a nutshell
ServletContext
lives for as long as the web app lives. It is shared among all requests in all sessions.HttpSession
lives for as long as the client is interacting with the web app with the same browser instance, and the session hasn't timed out at the server side. It is shared among all requests in the same session.HttpServletRequest
andHttpServletResponse
live from the time the servlet receives an HTTP request from the client, until the complete response (the web page) has arrived. It is not shared elsewhere.Servlet
,Filter
andListener
instances live as long as the web app lives. They are shared among all requests in all sessions.attribute
that is defined inServletContext
,HttpServletRequest
andHttpSession
will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as anattribute
of its closest matching scope.Thread Safety
That said, your major concern is possibly thread safety. You should now know that servlets and filters are shared among all requests. That's the nice thing about Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise be too expensive to recreate,
init()
anddestroy()
them for every single request.You should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's not thread-safe! The below example illustrates this:
See also:
会话
简而言之:网络服务器向每个访问者在其网站上发布一个唯一标识符第一次访问。访客必须带回该身份证件,以便下次能够被认出。该标识符还允许服务器正确地将一个会话拥有的对象与另一个会话拥有的对象分开。
Servlet 实例化
如果 load-on-startup 为 false:
如果load-on-startup为true :
一旦他进入服务模式并且处于最佳状态,相同 servlet 将处理所有其他客户的请求。
为什么每个客户端拥有一个实例不是一个好主意?想一想:您会为每一份订单雇用一名披萨师傅吗?这样做的话你很快就会破产。
但它也伴随着很小的风险。请记住:这个人将所有订单信息都放在口袋里:所以如果您对 Servlet 上的线程安全,他最终可能会向某个客户端发出错误的命令。
Sessions
In short: the web server issues a unique identifier to each visitor on his first visit. The visitor must bring back that ID for him to be recognised next time around. This identifier also allows the server to properly segregate objects owned by one session against that of another.
Servlet Instantiation
If load-on-startup is false:
If load-on-startup is true:
Once he's on the service mode and on the groove, the same servlet will work on the requests from all other clients.
Why isn't it a good idea to have one instance per client? Think about this: Will you hire one pizza guy for every order that came? Do that and you'd be out of business in no time.
It comes with a small risk though. Remember: this single guy holds all the order information in his pocket: so if you're not cautious about thread safety on servlets, he may end up giving the wrong order to a certain client.
Java servlet 中的会话与其他语言(例如 PHP)中的会话相同。它对于用户来说是唯一的。服务器可以通过不同的方式来跟踪它,例如 cookies、url 重写等。 html" rel="noreferrer">Java doc 文章在 Java servlet 的上下文中对此进行了解释,并指出会话的具体维护方式是留给服务器设计者的实现细节。该规范仅规定它必须在与服务器的多个连接中保持对于用户来说是唯一的。查看Oracle 的这篇文章了解更多信息关于你的两个问题。
编辑有一个很棒的教程此处介绍如何在 servlet 内使用会话。 这里是 Sun 关于 Java Servlet 的一章,它们是什么以及如何使用它们。在这两篇文章之间,您应该能够回答所有问题。
Session in Java servlets is the same as session in other languages such as PHP. It is unique to the user. The server can keep track of it in different ways such as cookies, url rewriting etc. This Java doc article explains it in the context of Java servlets and indicates that exactly how session is maintained is an implementation detail left to the designers of the server. The specification only stipulates that it must be maintained as unique to a user across multiple connections to the server. Check out this article from Oracle for more information about both of your questions.
Edit There is an excellent tutorial here on how to work with session inside of servlets. And here is a chapter from Sun about Java Servlets, what they are and how to use them. Between those two articles, you should be able to answer all of your questions.
当 servlet 容器(如 Apache Tomcat)启动时,如果出现任何问题或在容器端控制台显示错误,它将从 web.xml 文件(每个应用程序只有一个)中读取,否则,它将部署并加载所有 Web使用 web.xml(因此将其命名为部署描述符)的应用程序。
在 servlet 的实例化阶段,servlet 实例已准备就绪,但无法服务客户端请求,因为它缺少两条信息:
1:上下文信息
2:初始配置信息
Servlet引擎创建servletConfig接口对象将上面缺失的信息封装进去
servlet 引擎通过提供 servletConfig 对象引用作为参数来调用 servlet 的 init()。一旦 init() 完全执行,servlet 就准备好服务客户端请求。
问)在 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 开发人员来实现,即处理客户端的多个请求无需担心多线程代码
简短形式:
当应用程序启动时(部署在 servlet 容器上)或首次访问时(取决于启动时加载设置),将创建 servlet
当servlet被实例化时,会调用servlet的init()方法
然后servlet(它的唯一实例)处理所有请求(它的service()方法被多个线程调用)。这就是为什么不建议在其中进行任何同步,并且应该避免 servlet 的实例变量
当应用程序被取消部署(Servlet 容器停止)时,将调用 destroy() 方法。
When the servlet container (like Apache Tomcat) starts up, it will read from the web.xml file (only one per application) if anything goes wrong or shows up an error at container side console, otherwise, it will deploy and load all web applications by using web.xml (so named it as deployment descriptor).
During instantiation phase of the servlet, servlet instance is ready but it cannot serve the client request because it is missing with two pieces of information:
1: context information
2: initial configuration information
Servlet engine creates servletConfig interface object encapsulating the above missing information into it
servlet engine calls init() of the servlet by supplying servletConfig object references as an argument. Once init() is completely executed servlet is ready to serve the client request.
Q) In the lifetime of servlet how many times instantiation and initialization happens ??
A)only once (for every client request a new thread is created)
only one instance of the servlet serves any number of the client request ie, after serving one client request server does not die. It waits for other client requests ie what CGI (for every client request a new process is created) limitation is overcome with the servlet (internally servlet engine creates the thread).
Q)How session concept works?
A)whenever getSession() is called on HttpServletRequest object
Step 1: request object is evaluated for incoming session ID.
Step 2: if ID not available a brand new HttpSession object is created and its corresponding session ID is generated (ie of HashTable) session ID is stored into httpservlet response object and the reference of HttpSession object is returned to the servlet (doGet/doPost).
Step 3: if ID available brand new session object is not created session ID is picked up from the request object search is made in the collection of sessions by using session ID as the key.
Once the search is successful session ID is stored into HttpServletResponse and the existing session object references are returned to the doGet() or doPost() of UserDefineservlet.
Note:
1)when control leaves from servlet code to client don't forget that session object is being held by servlet container ie, the servlet engine
2)multithreading is left to servlet developers people for implementing ie., handle the multiple requests of client nothing to bother about multithread code
Inshort form:
A servlet is created when the application starts (it is deployed on the servlet container) or when it is first accessed (depending on the load-on-startup setting)
when the servlet is instantiated, the init() method of the servlet is called
then the servlet (its one and only instance) handles all requests (its service() method being called by multiple threads). That's why it is not advisable to have any synchronization in it, and you should avoid instance variables of the servlet
when the application is undeployed (the servlet container stops), the destroy() method is called.
会议 - 克里斯·汤普森所说的。
实例化 - 当容器收到映射到 servlet 的第一个请求时,servlet 就会被实例化(除非 servlet 配置为使用
在启动时加载)web.xml
中的 > 元素)。同一实例用于服务后续请求。Sessions - what Chris Thompson said.
Instantiation - a servlet is instantiated when the container receives the first request mapped to the servlet (unless the servlet is configured to load on startup with the
<load-on-startup>
element inweb.xml
). The same instance is used to serve subsequent requests.Servlet 规范 JSR-315 明确定义了服务(以及 doGet、doPost、doPut 等)方法中的 Web 容器行为(2.3.3.1 多线程问题,第 9 页):
The Servlet Specification JSR-315 clearly defines the web container behavior in the service (and doGet, doPost, doPut etc.) methods (2.3.3.1 Multithreading Issues, Page 9):
不。 Servlet 不是线程安全的
这允许一次访问多个线程
如果你想让 Servlet 成为线程安全的,你可以去对于
实现 SingleThreadInterface(i)
这是一个空白接口,没有
方法
,或者我们可以使用同步方法,
使用synchronized关键字使整个服务方法同步,
前面
我们可以通过在方法Example::
或者我们可以 将整个服务方法同步Synchronized 块中的代码
Example::
我觉得 Synchronized 块比使整个方法都
同步更好
No. Servlets are not Thread safe
This is allows accessing more than one threads at a time
if u want to make it Servlet as Thread safe ., U can go for
Implement SingleThreadInterface(i)
which is a blank Interface there is no
methods
or we can go for synchronize methods
we can make whole service method as synchronized by using synchronized
keyword in front of method
Example::
or we can the put block of the code in the Synchronized block
Example::
I feel that Synchronized block is better than making the whole method
Synchronized
从上面的解释可以清楚地看出,通过实现 SingleThreadModel,servlet 容器可以确保 servlet 的线程安全性。容器实现可以通过两种方式做到这一点:
1) 将请求序列化(排队)到单个实例 - 这类似于不实现 SingleThreadModel 但同步 service/ doXXX 方法的 servlet;或者
2) 创建实例池 - 这是一个更好的选择,也是 servlet 的启动/初始化工作/时间与托管 servlet 的环境的限制性参数(内存/CPU 时间)之间的权衡。
As is clear from above explanations, by implementing the SingleThreadModel, a servlet can be assured thread-safety by the servlet container. The container implementation can do this in 2 ways:
1) Serializing requests (queuing) to a single instance - this is similar to a servlet NOT implementing SingleThreadModel BUT synchronizing the service/ doXXX methods; OR
2) Creating a pool of instances - which's a better option and a trade-off between the boot-up/initialization effort/time of the servlet as against the restrictive parameters (memory/ CPU time) of the environment hosting the servlet.