代码之家  ›  专栏  ›  技术社区  ›  saugata

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

  •  11
  • saugata  · 技术社区  · 15 年前

    在Tomcat5.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");
        }   
    }
    
    1 回复  |  直到 15 年前
        1
  •  10
  •   Sean Owen    15 年前

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

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

    但对于其他的公共类(如J2EEAPI和实现)来说,这有点浪费。原则上,类不应该依赖于这个类加载器结构。

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

    你所观察到的行为是另一个不这样做的原因:应用程序之间的隔离度降低了。我不觉得这是违反直觉的行为,但我想这是因为我已经习惯了Tomcat的工作方式和思考这些事情。