我运行的是tomcat 6.0.23,根据类加载器documentation webapps 应按以下顺序查找类:
- Bootstrap
- 系统
- WEB-INF/类
- WEB-INF/lib
- 常见
我有一个使用 hibernate 的 web 应用程序,hibernate jar 位于它的 WEB-INF/lib 目录中。单独运行时,一切正常。
我还有一个 jar 文件需要位于 tomcat/lib 目录中,因为它包含一些需要在启动时加载的类(对象工厂及其创建的对象)。这些类使用 toplink 进行 JPA 实现,这就是我遇到问题的地方。
我需要将 toplink jar 放在可以在 tomcat 启动时访问的位置,因此我将它们放在 tomcat/lib 目录中。根据上面列出的类加载顺序,当使用hibernate的webapp想要hibernate实现类时,它应该在它的WEB-INF/lib目录中找到它们,但实际发生的情况是它从tomcat/lib中找到toplink实现类目录,我得到一个类转换异常。
谁能解释一下为什么我的 webapp 类加载器在 WEB-INF/lib 目录中找不到他们需要的东西,或者建议一种在运行时调试类路径的方法?
谢谢。
最佳答案
请务必阅读并理解您引用的列表前面的段落:
“当处理从 Web 应用程序的 WebappX 类加载器加载类的请求时,该类加载器将首先在本地存储库中查找,而不是在查找之前进行委托(delegate)。也有异常(exception)。属于 JRE 基础的类类不能被覆盖。对于某些类(例如 J2SE 1.4+ 中的 XML 解析器组件),可以使用 J2SE 1.4 认可的功能。”
您遇到问题的这些“hibernate 实现类”是什么? (Hibernate 的“实现”类与 Toplink 的完全不同。)它们是 javax.persistence 类吗?这些可能(也可能不)属于“JRE 基类” 类别,其行为不同。
编辑:根据您的评论,这只是一个典型的跨类加载器加载问题。 Tomcat 的类加载完全按照您的预期工作。如果您查看进行此初始化的 JPA 类,您会发现如下行:
Enumeration<URL> resources =
cl.getResources("META-INF/services/" + PersistenceProvider.class.getName());
这会从所有类加载器加载所有 PersistenceProvider,包括lib目录中的Toplink。然后它立即执行此操作:
for ( PersistenceProvider provider : providers ) { ...
这是 javax.persistence.Persistence 的第 77 行,您的异常来自于此。这是因为该行引用的 PersistenceProvider 类来自您的 webapp 类加载器,但该集合包含两个实例:来自同一类加载器的 Hibernate 实现和来自不同类加载器的 Toplink 实例。
这种全局静态初始化是阻止我转向 JPA 的一大原因。由于这样的问题,我仍然直接使用 Hibernate。
关于java - Tomcat 类加载的行为似乎与记录的不符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7337046/