6
  • 在我的/tomcat/lib目录中,我有课SharedClass(因此与所有网络应用程序共享)。
  • 在我的网络应用程序中,WEB-INF/lib我有 class LocalClass
  • SharedClass有参考LocalClass

在我的网络应用程序中,我尝试创建一个实例,SharedClass但它失败并显示以下消息:

NoClassDefFoundError:本地类。

由于SharedClass是共享的并且LocalClass是我的网络应用程序的本地应用程序,我希望它可以工作,但它没有。

我的怀疑是SharedClass由Tomcat父类加载器LocalClass加载并由Web App类加载器加载。由于SharedClass是由父级加载的,我假设它的所有依赖项也必须由父级加载。因此,父级找不到LocalClass并抛出错误。

这有意义吗?有没有办法解决这个问题(不写我自己的类加载器)?

4

3 回答 3

7

类加载器是分层的。类加载器有一个父级,并且可以看到其父级的类。反过来是不正确的。因此,由 webapp 的类加载器加载的类可以访问由通用 Tomcat 类加载器(其父类)加载的类,后者可以访问 JRE 类(Tomcat 类加载器的父类)。

有关更多详细信息,请参阅Tomcat 文档ClassLoader javadoc

于 2013-01-11T22:13:00.430 回答
3

这确实是有道理的,这也是 Tomcat 能够隔离应用程序的方式。更多信息可以在这里找到:http: //tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

共享类加载应用程序类或创建应用程序类实例的正当理由很少。对于需要执行此操作的极少数情况,您可以调用Thread.getCurrentThread().getContextClassloader().


编辑:我觉得我正在将浸过 LOX 的煤球交给想要开始篝火的人,所以这里是为什么从共享类加载器加载应用程序类是一个坏主意的原因。

直接的问题是您实际上并未将类加载到共享类加载器中,因此您无法轻松地操作您创建的对象。相反,您需要使用反射来调用方法,这会使您的代码变得一团糟。

但更糟糕的问题是,如果您的SharedClass实例维护对 的实例的引用LocalClass,这些引用将阻止 Tomcat 取消部署应用程序。实际上,它会声称取消部署,但在这些引用被收集之前,仍然会有旧应用程序的碎片存在于 permgen 中,这通常会导致 permgen 耗尽。

因此,虽然上下文类加载器是一个有用的工具——并且应该是您访问类路径资源的唯一方式——但它是一个很容易导致事情崩溃的工具。

于 2013-01-11T22:13:08.737 回答
0

在普通的 Java 应用程序中,当要求类加载器加载一个类时,它首先将请求委托给它的父类加载器,然后在父类加载器找不到请求的类时加载它。

对于 Web 应用程序服务器,这略有不同。对于部署在像 tomcat 这样的 Web 应用程序服务器中的每个 Web 应用程序,通常有不同的类加载器。对于 Tomcat,它如下所示 -

在此处输入图像描述

因此,对于 Web 应用程序类加载资源按以下顺序发生 -

  1. JVM 的引导类(核心 java 类)
  2. /WEB-INF/Web 应用程序的类
  3. /WEB-INF/lib/*.jar 您的 Web 应用程序
  4. 系统类加载器类(Tomcat / Classpath 特定类)
  5. 通用类加载器类(所有 Web 应用程序通用的类)

但请注意,如果配置了 Web 应用程序类加载器,delegate="true"则顺序会更改 -

  1. JVM 的引导类(核心 java 类)
  2. 系统类加载器类(Tomcat / Classpath 特定类)
  3. 通用类加载器类(所有 Web 应用程序通用的类)
  4. /WEB-INF/Web 应用程序的类
  5. /WEB-INF/lib/*.jar 您的 Web 应用程序

有关更多详细信息,您可以查看 Apache Tomcat 的Class Loader HOW-TO页面。

于 2015-02-22T16:08:28.077 回答