如何解决 Tomcat 类加载问题?

问题描述 投票:0回答:1

我的 Tomcat 服务器上有三个 Web 应用程序。我使用 ActiveMQ 在它们之间发送消息。这已经有效,但现在我正在尝试从 5.14 更新到 5.18,并且遇到了类加载问题。

为了节省 WAR 大小,我在全局 tomcat /lib 目录中放置了 activemq-all jar,这在我运行 5.14 时有效。

但是现在在 5.18 中发生的情况是,我在所有 war lib 文件中都有一个 jar 文件中的类,但出现了 ClassNotFound 错误:

原因:java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper 位于 org.apache.activemq.broker.jmx.PersistenceAdapterView。(PersistenceAdapterView.java:31) 在 org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter.doStart(KahaDBPersistenceAdapter.java:235)

我不擅长类加载,但由于某种原因,当 ActiveMQ 在我的一个 Web 应用程序中设置持久性时,该类似乎是从“中央”lib 文件夹加载的,这意味着它不能使用我的战争中存在什么阶级?

我不明白,因为加载试图加载ObjectMapper的activeMQ类的类加载器必须是我的应用程序类加载器,那么为什么它找不到ObjectMapper类?

所以,我的问题:

  1. 为什么我的应用程序不能使用我的 war 和 tomcat/lib 中的所有类?难道不是同一个类加载器,因为是我的应用程序触发了加载?

  2. 我是用 activeMQ 类发送每场战争的唯一解决方案吗?它有效,我尝试过,但是 WAR 文件变得更大......

非常感谢指点。

java tomcat activemq-classic classloader
1个回答
0
投票

您的情况涉及 Java 类加载的一些基本方面,特别是在像支持多个应用程序的 servlet 容器 (Tomcat) 这样的复杂环境中。让我们一次解决一个问题:

1.了解 Tomcat 中的类加载器

在 Tomcat 中,通常有三个类加载器在起作用:

  • Bootstrap 类加载器:加载核心 Java 类(从 JRE lib 目录)。
  • 系统类加载器:加载 Tomcat 及其所有应用程序可用的类(来自 TOMCAT_HOME/lib 目录)。
  • Webapp 类加载器:每个应用程序独有;从 web 应用程序的 WEB-INF/classes 和 WEB-INF/lib 目录加载类。

当您将 JAR 放入 TOMCAT_HOME/lib 时,它会由系统类加载器加载并在所有应用程序之间共享。这对于不依赖于 webapp 类加载器中的类的库来说效果很好。但是,如果这些库需要使用 webapp 类加载器加载的类,则可能会出现

ClassNotFoundException
NoClassDefFoundError
等问题。

2. ActiveMQ 和 ObjectMapper 的类加载问题

在您的例子中,

activemq-all.jar
位于Tomcat全局lib目录中。这意味着它是由系统类加载器加载的。但是,Jackson 库中的
ObjectMapper
类可能位于您的 web 应用程序的
WEB-INF/lib
中,由 web 应用程序类加载器加载。 Java 类加载器使用委托模型,其中类加载器将要求其父级加载一个类,然后再尝试加载它自己。但是较高级别的类加载器(如系统类加载器)无法看到较低级别的类加载器(如 webapp 类加载器)加载的类。

因此,当 ActiveMQ 尝试加载和使用

ObjectMapper
时,它无法找到它,因为系统类加载器无法查看
ObjectMapper
所在的 webapp 类加载器。

3.解决方案

a.将 Jackson 移至 TOMCAT_HOME/lib

解决此问题的一种方法是确保 ActiveMQ 所需的所有依赖项与 ActiveMQ 本身处于相同的类加载级别。您可以将 Jackson 库移至 TOMCAT_HOME/lib 目录。这应该可以解决类加载问题,因为 ActiveMQ 和 Jackson 将由同一个类加载器加载。

b.将 ActiveMQ 与每个应用程序捆绑在一起

正如您所指出的,另一种解决方案是将

activemq-all.jar
及其依赖项捆绑在每个应用程序的
WEB-INF/lib
目录中。这种方法之所以有效,是因为 ActiveMQ 所需的所有内容都是由同一个类加载器加载的。但是,这会增加每个 WAR 文件的大小并在应用程序之间重复库。

c.使用自定义类加载器

更复杂的解决方案涉及在 Tomcat 中配置自定义类加载器,这可以帮助更精细地管理类可见性。这是相当先进的,通常仅在标准解决方案不够时才推荐。

4.推荐

我建议从第一个解决方案开始:将所有必需的库放在 TOMCAT_HOME/lib 目录中。这通常是解决此类问题的最简单、最有效的方法,而无需对每个应用程序进行大量修改。它确保系统类加载器可以使用所有必需的类并简化维护。

确保在生产环境中部署此配置之前,在临时环境中对其进行测试,以验证所有组件是否正确交互并且不会引入新问题。

© www.soinside.com 2019 - 2024. All rights reserved.