配置Tomcat使每个应用程序只能启动一个Servlet

3

据我理解,Tomcat允许创建尽可能多的servlet副本以处理请求。

这迫使我的servlet没有重量级状态,而是将所有状态存储在(单例)servlet上下文中。

是否可以配置Tomcat将我的servlet视为单例,并始终只创建一个servlet实例,而不考虑负载?

4个回答

11
根据Servlet规范(v2.4,第2.2节“实例数量”):
SRV.2.2 实例数量
对于未托管在分布式环境中的servlet(默认情况下),Servlet容器必须每个servlet声明仅使用一个实例。但是,对于实现SingleThreadModel接口的servlet,Servlet容器可以实例化多个实例来处理重负载请求并将请求序列化到特定实例。
回答您的问题很简单:不要实现SingleThreadModel,也不要在web.xml描述符文件中声明您的servlet多次。

相关问题 - 在调用destroy()方法后,容器是否保证不会保留对servlet的引用? - ripper234
1
是的。请查看SRV.2.3.4节,服务结束:“一旦在servlet实例上调用destroy方法,容器就不能将其他请求路由到该servlet实例。如果容器需要再次启用servlet,则必须使用servlet类的新实例来执行此操作。在destroy方法完成后,servlet容器必须释放servlet实例,以便它可以进行垃圾回收。” - Peter Štibraný

2
另一个不想在Servlet中存储状态的主要原因是它会引入同步问题、序列化问题(如果你想在集群环境中运行),等等。如果你不在servlet中内部存储状态,而是将其存储在Session(或Request)对象中,所有这些问题都很容易避免。所以真正的问题是...为什么你想存储状态?这是一个“推荐做法”的原因。

1

不,我不这么认为。然而,在实践中,即使JavaEE规范允许它实例化多个servlet,但tomcat只会实例化一个servlet。大多数(如果不是全部)MVC框架都依赖于这个假设,并在servlet中存储了大量的配置状态。

如果你想保险起见,那么不要将你的状态存储在servlet中,而是将其放在另一个使用单例模式的类中,并从你的servlet引用该单例。


1
我喜欢Spring MVC - 它可以作为相当轻量级的servlet包装器,但是提供了许多好东西,包括依赖注入,这样你就可以将共享对象/服务分配给控制器(相当于servlet)而不是硬编码它们(这样使测试和维护更加方便)。尝试教程或阅读像《Spring实战》这样的书来入门。像分离视图一样,分离控制器和模型逻辑对于良好的MVC设计同样重要(我认为你的共享状态和服务在这里算作“模型”)。 - paulcm
1
"JavaEE规范允许它实例化多个" - 这取决于情况。一个容器(即tomcat)每个声明只能实例化单个servlet,除非servlet实现了SingleThreadModel接口。 - Peter Štibraný
单线程模型确保每个线程一个实例,但我不认为省略它能够保证每个容器一个实例。 - skaffman
1
Servlet规范保证每个容器只有一个实例,除非您使用SingleThreadModel。在这种情况下,容器允许多次实例化servlet。 - Peter Štibraný

1
同 Matt B 一样。我曾经在没有学到这个教训之前被严重烧伤过。我在 servlet 对象的静态变量中存储了状态信息。在我们只有一个服务器的测试环境中,所有东西都非常顺利。然后我们部署到生产环境中,遇到了非常奇怪的错误,最终才弄清楚。
使用 Session 或 Application 对象。这就是它们的用途。
寓意:当结构的设计者提供一个工具并说要在做 X 时使用此工具时,很可能有理由这样做。非常小心地忽视这样的建议。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接