Difference between each instance of servlet and each thread of servlet in servlets?
servlet类有多个实例吗?正如我听到的"servlet的每个实例"有人能详细解释一下吗?
- 不!Web容器只在第一次请求时通过调用newInstance()方法创建servlet的一个实例。
当servlet容器启动时,它:
读取web.xml;
在类路径中查找声明的servlet;以及
只加载和实例化每个servlet一次。
大致如此:
1 2 3 4 5
| String urlPattern = parseWebXmlAndRetrieveServletUrlPattern ();
String servletClass = parseWebXmlAndRetrieveServletClass ();
HttpServlet servlet = (HttpServlet ) Class. forName(servletClass ). newInstance();
servlet. init();
servlets. put(urlPattern, servlet ); // Similar to a map interface. |
这些servlet存储在内存中,并在每次请求URL与servlet关联的url-pattern匹配时重复使用。servlet容器随后执行类似以下代码:
1 2 3 4 5 6 7 8
| for (Entry <String, HttpServlet > entry : servlets. entrySet()) {
String urlPattern = entry. getKey();
HttpServlet servlet = entry. getValue();
if (request. getRequestURL(). matches(urlPattern )) {
servlet. service(request, response );
break;
}
} |
轮到GenericServlet#service()决定doGet()和doPost()中的哪一个。基于HttpServletRequest#getMethod()调用。
可以看到,servletcontainer为每个请求重用相同的servlet实例。换句话说:servlet在每个请求之间共享。这就是为什么编写servlet代码的方式非常重要——实际上很简单:不要将请求或会话范围的数据分配为servlet实例变量,而是将其作为方法局部变量。例如。
1 2 3 4 5 6 7 8 9 10 11
| public class MyServlet 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.
}
} |
- + 1。我要补充的是,如果在web.xml中将同一个servlet类映射到两个不同的URL,那么将创建两个实例。但是,一般原则仍然适用,一个实例服务于多个请求。
- @Balusc,只是想知道,有没有一种方法可以从Web应用程序访问启动的servlet集?
- 类中的变量thisIsNotThreadSafe是共享给不同的用户还是只共享给同一用户的不同页面?我的意思是,如果我在我的计算机上浏览这个页面,而你在你的计算机上运行,我们是否共享这个非线程安全的相同空间?谢谢。
- 是的,@closedow,如果您的意思是两个客户机都访问同一个JVM,那么应该只有一个servlet,因此私有财产thisIsNOTThreadSafe 将在会话之间共享。即使您注销并再次登录。
- 您好,我的理解是这些servlet实例存储在JVM中,当请求出现时,servlet容器会在JVM中搜索这些实例。这是正确的吗?
- 以上3个步骤是否都在容器启动时运行?表示servlet在容器启动时初始化了吗?那么为什么我们要使用"启动时加载"?
- @未知:stackoverflow.com/q/3106452
- @asifmushtaq否,容器在启动时不预加载servlet。这个过程是相同的,只是它可能在第一次请求时发生,而不是在启动时发生。工具允许您清楚地知道,某些servlet应该在启动时加载,特别是以您希望它们初始化的顺序加载。
不,servlet只有一个实例,可用于来自多个客户机的多个请求。这导致了两个重要的规则:
- 不要在servlet中使用实例变量,应用程序范围的值除外,这些值通常是从上下文参数获得的。
- 不要在servlet中生成方法synchronized。
(servlet过滤器和jsp也是如此)
根据JavaServlet规范3版(pp.6-7),除非JavaRead模型实现每个JVM的多个实例,否则每个JVM每个声明将有一个实例。
- servlet规范3。(第6-7页)关于单线程模型的第2.2.1节注释中指出,"本规范版本中不推荐使用单线程模型接口。"
- @reva虽然该接口确实被弃用了,但几乎可以肯定,它将永远不会被删除,容器将无限期地支持它。一旦成为规范的一部分,就始终是规范的一部分。
虽然已经有一些好的答案,但他们中没有人谈到部署在分布式环境中的Java Web应用程序。这是一个实际的场景,其中实际创建了一个servlet的多个实例。在分布式环境中,您有一个机器集群来处理请求,并且请求可以转到这些机器中的任何一台。这些机器中的每一台都应该能够处理请求,因此每台机器都应该在它的JVM中有一个myawesomeservlet实例。
因此,正确的语句应该是每个servlet的每个JVM只有一个实例,除非它实现了singlethreadmodel。
简单来说,singlethreadmodel表示,每个servlet实例只能有一个线程,因此基本上需要为每个即将到来的请求创建一个实例来处理它,这基本上扼杀了以并行方式处理请求的整个概念,并且作为servlet对象的创建和初始化并不被认为是一个好的实践。在准备好处理请求之前需要花费一些时间。
- 如果你真的想变得更加学究(很明显你是这样做的),那么servlet的唯一性不是每个JVM而是每个上下文。您可以在多个上下文路径下(在一个或多个JVM中)自由地部署同一个应用程序,并且每个JVM都将具有该servlet的实例。
- @克里斯托弗舒尔茨:我不太明白,你能给我指一些材料来更好地理解它吗?谢谢。
- 如果myawesomeservlet是应用程序战争的一部分,并且您将其部署到两个上下文(例如/foo和/bar中),那么您将不得不复制MyAwesomeServlet类定义,实际上,还必须同时在内存中复制MyAwesomeServlet的两个实例。因此,实际上每个JVM可以有多个servlet实例。注意,JVM认为这些是单独的类,因为它们是由单独的类加载器加载的。Servlet规范中需要一个单独的类加载器,一般来说,爪哇中的概念类="CultLoope+类"。
servlet类不能有多个实例。即使有一个servlet实例,它也能够处理多个请求。所以最好不要使用类级变量。
对于那些知道真正的javascript(不仅仅是它的库)的用户,servlet可以被看作是函数对象。作为功能对象,它们的主要任务是做一些事情,而不是在胸部存储一些信息。不需要实例化每个这样的函数对象的一个实例,其原理与Java类方法在该类的所有实例之间共享的原理相同。