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


在Tomcat 5.5服务器上,我在系统类路径中放了一个类(并修改catalina.bat来选择它),或者如果我将类放在共享的lib目录中。现在,如果我有两个不同的应用程序使用相同的类,它们的WEB-INF lib / classes目录中没有该类,则它们使用该类的相同实例。我理解一个类加载器将委托给它的父类加载器的概念,如果它找不到它,那么在这种情况下,由于该类不存在于WEB-INF / classes或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");
    }   
}

1416
2018-03-10 11:39


起源

我不明白这个问题:你自己解释说Tomcat的系统类加载器中的类是在所有应用程序之间共享的。因此,两个应用程序访问同一个类,从而从相同的静态字段读取,并可以看到彼此的影响。 - Joachim Sauer
是的,但对我而言,两个独立的应用程序可以以这种方式共享上下文。实际上,如果它们具有相同的类实例,它们甚至可以跨两个不同的应用程序实现多线程/同步,这看起来非常违反直觉。 - saugata


答案:


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

关键是ClassLoader的结构。一个JVM中的两个ClassLoader完全可以加载一个类,因此包含静态字段的独立,独立副本。 “static”为ClassLoader而不是JVM创建了“全局”。我想,Tomcat无法容纳带有共享库的容器级ClassLoader,并以某种方式强制每个应用程序ClassLoader分别加载共享库。

但是对于J2EE API和实现等其他常见类来说,这会有点浪费。原则上,类不应该依赖于这个ClassLoader结构。

这就是您不应将应用程序依赖项放在Tomcat的共享库文件夹中的原因。这就是'解决方案'。它将您的应用程序与容器的特定设置和部署联系起来,这违反了J2EE Web应用程序的原则。只需在WEB-INF / lib中为每个应用程序放置依赖项的副本。

您观察到的行为是不这样做的另一个原因:应用程序彼此之间的隔离程度降低。它不会让我觉得是反直觉的行为,但我认为这只是因为我已经习惯了Tomcat如何运作并思考这些事情。


10
2018-03-10 13:02



我有一个类似的问题,我需要在两个webapps之间共享对象。在我看来,将对象放在系统类加载器中可能是唯一的解决方案。看到 stackoverflow.com/questions/9453109/... - ziggy
也许......但是在webapps之间共享绝对不是你想要做的事情。 (请查看.ear文件)由于上述原因,它可能会发生工作但不能保证正常工作。 - Sean Owen