一尘不染

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

tomcat

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

但是,这让我觉得很奇怪,两个不同的应用程序可以使用此方法共享上下文。有人可以帮助我理解为什么会这样。例如,在下面的代码中,当共享CommonCounter时,两个servlet分别部署在不同的环境中,它们可以读取彼此递增的计数器值。

编辑

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

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");
    }   
}

阅读 221

收藏
2020-06-16

共1个答案

一尘不染

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

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

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

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

您观察到的行为是不执行此操作的另一个原因:应用程序之间的隔离度降低了。这并没有使我成为违反直觉的行为,但这只是因为我已经习惯了Tomcat如何工作并思考这些事情。

2020-06-16