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

5
  • 我在/tomcat/lib目录下有一个类SharedClass(这个类被所有的Web应用程序共享)
  • 我在我的Web应用程序的WEB-INF/lib目录下有一个类LocalClass
  • SharedClass引用了LocalClass

我尝试创建SharedClass的一个实例,但是失败了,并显示以下消息:

NoClassDefFoundError: LocalClass.

由于SharedClass是被共享的,而LocalClass是本地的,所以我希望它能够正常工作,但是它没有。

我怀疑SharedClass是由Tomcat父类加载器加载的,而LocalClass是由Web应用程序类加载器加载的。由于SharedClass是由父级加载的,我认为它的所有依赖项也必须由父级加载。因此,父级找不到LocalClass并抛出错误。

这有道理吗?有没有什么方法可以解决这个问题(而不编写自己的类加载器)?


经过所有的编辑之后,似乎SharedClass直接引用了LocalClass。在这种情况下,我展示的上下文类加载器技巧将不起作用,您需要将JAR文件打包在一起。 - parsifal
@dough:不行。SharedClass是由Tomcat类加载器加载的,而LocalClass对于这个类加载器是不可访问的:它不在Tomcat类路径中。 - JB Nizet
@dough - 简单的反射无法工作,因为JB Nizet所说的原因。您可以使用上下文类加载器加载类,然后使用反射在其上调用方法。但是,这是一个非常糟糕的想法。 - parsifal
请描述您实际想要实现的内容,因为几乎肯定有更简单的方法,而这种方法不会违反Tomcat努力确保的应用程序隔离。 - parsifal
@dough - #1,第二名是#2(因为应用程序实际上应该是自包含的)。此外,请查看我对帖子所做的编辑。 - parsifal
显示剩余3条评论
3个回答

7

类加载器是分层的。一个类加载器有一个父级,可以看到其父级的类。反之则不成立。因此,由Web应用程序类加载器加载的类可以访问由公共Tomcat类加载器(其父级)加载的类,后者可以访问JRE类(Tomcat类加载器的父级)。

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


3
这是有道理的,这也是Tomcat能够隔离应用程序的方式。更多信息可以在这里找到:http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html 共享类加载器很少有有效的原因去加载应用程序类或创建应用程序类实例。对于你需要这样做的罕见情况,你可以调用Thread.getCurrentThread().getContextClassloader()
编辑:我觉得我正在把一个浸泡过火箭燃料的煤块交给一个想点篝火的人,所以这里列出了为什么从共享类加载器加载应用程序类是一个坏主意的原因。
直接问题在于,你没有将类加载到共享类加载器中,所以你不能轻松地操作你创建的对象。相反,你需要使用反射来调用方法,这会让你的代码混乱不堪。
但更糟糕的问题是,如果你的SharedClass实例保持对LocalClass实例的引用,那些引用将阻止Tomcat卸载应用程序。其实,它会声称已经卸载了,但旧的应用程序仍然存在于permgen中,直到这些引用被收集,这经常导致permgen耗尽。
因此,虽然上下文类加载器是一个有用的工具——也应该是你访问类路径资源的唯一方式——但这是一个可以轻松导致事情爆炸的工具。

这并不是一个不寻常的情况。我有一个外部依赖(ehcache)作为Tomcat共享依赖项,但该依赖项又依赖于其他依赖项(slf4j等)。我的唯一选择是将传递依赖项也移动到共享目录中。你有更好的建议吗? - Yamcha

0
在普通的Java应用程序中,当类加载器被要求加载一个类时,它首先将该请求委派给其父类加载器,然后在父类加载器无法找到所请求的类时才会加载它。
对于Web应用程序服务器,情况略有不同。通常为Web应用程序服务器中部署的每个Web应用程序提供不同的类加载器,例如Tomcat。对于Tomcat,它看起来像下面这样 -

enter image description here

因此,对于 Web 应用程序,类加载资源的顺序如下:

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

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接