是否所有类的依赖项都由同一个类加载器加载?

时间:2013-01-11 22:04:55

标签: java class tomcat web-applications classloader

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

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

  

NoClassDefFoundError:LocalClass。

由于SharedClass是共享的,LocalClass是我的网络应用程序本地的,我希望它可行,但事实并非如此。

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

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

3 个答案:

答案 0 :(得分:7)

ClassLoader是分层的。类加载器具有父级,并查看其父级的类。反之则不然。因此,webapp的类加载器加载的类可以访问由公共Tomcat类加载器(其父级)加载的类,它可以访问JRE类(Tomcat的类加载器的父级)。

有关详细信息,请参阅the Tomcat documentationClassLoader javadoc

答案 1 :(得分:3)

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

共享类加载应用程序类或创建应用程序类实例的有效原因很少。对于您需要执行此操作的极少数情况,您可以致电Thread.getCurrentThread().getContextClassloader()


编辑:我觉得我正在向一个想要开始篝火的人交出一个LOX浸泡的煤球,所以这就是从共享的类加载器加载应用程序类是个坏主意的原因

当前的问题是您实际上没有将类加载到共享类加载器中,因此您无法轻松地操作您创建的对象。相反,您需要使用反射来调用方法,这将使您的代码变得混乱。

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

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

答案 2 :(得分:0)

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

对于Web应用程序服务器,这略有不同。对于像tomcat这样的Web应用程序服务器中部署的每个Web应用程序,通常都有不同的类加载器。对于Tomcat,它看起来像下面 -

enter image description here

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

  1. JVM的引导类(核心java类)
  2. / WEB-INF /您的Web应用程序的类
  3. 您的Web应用程序的
  4. / WEB-INF / lib / * .jar
  5. 系统类加载器类(Tomcat / Classpath特定类)
  6. 常见的类加载器类(所有Web应用程序通用的类)
  7. 但请注意,如果Web应用程序类加载器配置了delegate="true",则订单会更改 -

    1. JVM的引导类(核心java类)
    2. 系统类加载器类(Tomcat / Classpath特定类)
    3. 常见的类加载器类(所有Web应用程序通用的类)
    4. / WEB-INF /您的Web应用程序的类
    5. 您的Web应用程序的
    6. / WEB-INF / lib / * .jar
    7. 有关详细信息,您可以查看Apache Tomcat的Class Loader HOW-TO页面。