具有多个应用程序的Tomcat上的类加载器行为

2022-09-04 21:48:33

在Tomcat 5.5服务器上,我将一个类放在系统类路径中(并修改catalina.bat来选择它),或者如果我把类放在共享的lib目录中。现在,如果我有两个不同的应用程序使用同一个类,而这些类在其 WEB-INF lib/classes 目录中没有该类,则它们使用相同的类实例。我理解这样一个概念,即如果找不到类,类装入器将委托给它的父类装入器来查找类,因此在这种情况下,由于该类不存在于WEB-INF/类或WEB-INF/lib中,因此WebAppX类装入器将分别尝试共享,公共和系统类装入器。

然而,这对我来说似乎很奇怪,两个不同的应用程序可以使用这种方法共享上下文。有人能帮我理解为什么会这样吗?例如,在下面的代码中,两个servlet各自部署在单独的战争中,而CommonCounter是共享的,它们可以读取另一个递增的计数器值。

编辑

在我看来,两个独立的应用程序可以以这种方式共享上下文似乎是违反直觉的。事实上,如果它们具有相同的类实例,它们甚至可以在两个不同的应用程序之间实现多线程/同步,这似乎非常违反直觉。

package com.test;
public class CommonCounter {

    public static int servlet1;
    public static int servlet2;
}




public class Servlet1 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        CommonCounter.servlet1++;
        System.out.println("Other one had "+CommonCounter.servlet2+" hits");
    }   
}



public class Servlet2 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        CommonCounter.servlet2++;
        System.out.println("Other one had "+CommonCounter.servlet1+" hits");
    }   
}

答案 1

正如评论所说,你已经正确地解释了为什么你观察到了你观察到的行为。

关键是类加载器的结构。一个 JVM 中的两个 ClassLoaders 完全有可能为每个类加载一个类,因此包含静态字段的单独、独立的副本。“static”使某些东西对ClassLoader而不是JVM是“全局的”。我想,Tomcat 无法拥有具有共享库的容器级 ClassLoader,并且以某种方式强制每个应用程序 ClassLoader 单独加载共享库。

但是对于其他常见的类(如 J2EE API 和实现)来说,这有点浪费。原则上,类无论如何都不应该依赖于这个ClassLoader结构。

这就是为什么你不应该把应用程序依赖关系放在Tomcat的共享库文件夹中。这就是“解决方案”。它将应用程序与容器的特定设置和部署相关联,这违背了 J2EE Web 应用程序的原则。只需将每个应用程序的依赖项副本放在 WEB-INF/lib 中即可。

您观察到的行为是不这样做的另一个原因:应用程序彼此之间的隔离程度降低。这对我来说并不是反直觉的行为,但我想这只是因为我习惯了Tomcat的工作方式并思考这些事情。


答案 2

推荐