10

环境是 Linux 上的 WAS 6.1,部署了一个使用来自 xercesImpl.jar 的类的 web 应用程序。

由于公司政策限制,必须使用以下设置部署应用程序:

Class Loader Order
    Classes loaded with parent class loader first
->  Classes loaded with application class loader first

WAR class loader policy
    Class loader for each WAR file in application
->  Single class loader for application

WAR 文件包含 xercesImpl.jar 的副本,与编译应用程序时类路径中的副本相同。

启动 webapp 时,当 Spring 尝试解析其配置时,它会抛出:

java.lang.VerifyError: class loading constraint violated 
    (class: org/apache/xerces/jaxp/DocumentBuilderImpl 
    method: parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;)

到目前为止的分析

看起来 WAS 提供了 org.apache.xerces.jaxp.DocumentBuilderImpl 的实现,因为我们可以从 WAR 文件中删除 xercesImpl.jar 并且仍然得到相同的错误(不是 ClassNotFoundException)。因此,WAS 似乎正在使用它自己的副本来解析引用,该副本与我们编译的类文件中的引用不兼容。但是,我能找到的唯一其他“xercesImpl.jar”实例(与我们的应用程序一起部署的副本除外)位于 directory deploytool中,这似乎在应用程序服务器之外。

我扫描了 WAS 中的所有罐子(全部 1300 个)

for i in `find . -name \*.jar`; do jar tvf $i|grep -qi xerces && echo $i ; done

并发现它./java/jre/lib/xml.jar包含 中的所有类org.apache.xerces.*,因此这很可能是类加载器解析引用的地方。

这是奇怪的部分:

如果我们更改为“先加载父类加载器”,我们看不到异常。这与预期的行为背道而驰。我们希望“应用程序类加载器优先”它会使用我们提供的 xercesImpl.jar,并且只有在我们设置“父类加载器优先”时才使用 WAS 的版本。这似乎与我们实际看到的相反。

问题:

类加载器委托设置如何与上述信息交互以产生观察到的行为?

4

6 回答 6

14

您的 WAR 还包括 org.xml.sax 或 org.w3c.dom 类,然后您引用的应用程序外部的类也引用了这些类。这设置了一个场景,您的应用程序类加载器可以看到同一类的两个实例,这是一个链接错误。

例如,如果您的应用程序使用 javax.xml.bind.Unmarshaller.unmarshal(InputSource),那么 Unmarshaller 将从 JDK 加载,并且 Unmarshaller 类仅对 JDK InputSource 可见。当您的应用程序创建其 InputSource 时,它​​将从 WAR 加载类(因为“应用优先”策略),然后您的应用程序将尝试将 WAR InputSource 的实例传递给 JDK Unmarshaller,后者只能接受JDK 输入源。

有两种解决方案:

  1. 从您的应用程序中删除所有 API jar,并使用 JDK 中的那些。例如,删除包含 org.xml.sax 或 org.w3c.dom 的 jar。
  2. 在您的 WAR 中包含所有引用您要引用的类的库。例如,在您的 WAR 中包含 JAXB 库的副本。

根据我的经验,链接错误很难追踪,因为 JVM 提供了关于导致添加链接的原因的糟糕信息。我通常启用类加载器跟踪,重现问题,然后向后走,直到找到从应用程序外部加载的类,“听起来”它可能引用了一个已知存在于应用程序内的类。

于 2010-05-19T00:00:25.243 回答
0

我们的问题是在 WAS 8.5 上部署。

在我们的 Web 应用程序中,我们有一个由 cxf 生成的 Web 服务客户端。没有问题。

当我们在 tika-parser 中添加用于 mime 类型检测时,我们遇到了这个问题。

我们排除了三个依赖项:

<dependency>
            <groupId>org.apache.tika</groupId>
            <artifactId>tika-parsers</artifactId>
            <version>${apache.tika.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>geronimo-stax-api_1.0_spec</artifactId>
                    <groupId>org.apache.geronimo.specs</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>xercesImpl</artifactId>
                    <groupId>xerces</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>xmlbeans</artifactId>
                    <groupId>org.apache.xmlbeans</groupId>
                </exclusion>
            </exclusions>
        </dependency>

一旦它们被排除,我们的应用程序就成功启动了。

于 2014-09-24T02:35:47.563 回答
0

可能为时已晚,但是我们解决了这个问题,在 server.xml 中删除了这一行:

jaxb-2.1

于 2018-03-28T18:16:49.800 回答
0

这是发生了什么:

在应用程序的 pom.xml 文件和WAS 上的 server.xml 文件中都定义了相同的库。因此,在 WAS 从应用程序的 pom.xml 文件创建的 war 文件加载类之后,它会尝试从在 server.xml 文件中指定的 WAS 上的共享库中的库中加载相同的类。

如果您的应用程序被命名为 corrservo65-01,则 server.xml 文件将为应用程序定义在由如下路径定义的文件夹中:

/usr/WebSphere905/AppServer/profiles/node01/config/cells/dwauslwasapp06Cell01/nodes/dwauslwasapp06Node01/servers/corrservo65-01

您可以通过在 server.xml 文件中搜索 classloaders 元素来查看哪些库也正在从共享库中加载。在类加载器元素内部有库元素。每个都定义了一个库,该库也从 WAS 上的共享库以及应用程序的 pom.xml 文件中定义的库加载

例如,wsdl4j-1.6.2 被定义为 pom.xml 文件中的依赖项:

 <dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
    <version>1.6.2</version>
</dependency>

这是 WAS 从共享库中加载的同一个库:

<classloaders xmi:id="Classloader_1629835928681" mode="PARENT_LAST">
   <libraries xmi:id="LibraryRef_1629835942246" libraryName="wsdl4j-1.6.2"/>
</classloaders>

当您在 WAS 中将应用程序设置为 parent_last 时,您还必须从中删除对在共享库中找到的库的引用。然后,您需要将这些库包含在应用程序的 pom.xml 文件中。这将导致基于 pom.xml 文件加载库。

但是,还有另一个可能的来源,那就是 WAS JDK。如果该库位于 WAS JDK 中,那么它将始终从那里加载。在这些情况下,您必须从 pom.xml 文件中删除依赖项或将范围设置为“已提供”。将范围设置为“已提供”将在应用程序在本地编译时使用依赖项,但在将其部署到 WAS 时则不会。

最后,有一个用于 WAS 的类加载器和一个用于 JDK 的类加载器。如果两者都包含相同的类,比如 javax.server.ServletContext,那么它们仍然会出现异常——即使该类在 ear 文件中不存在。

在我们的特殊情况下,我们必须像这样设置 WAS:类加载器使用 parent_last,托管模块使用 parent_first。

于 2022-01-21T17:30:45.527 回答
-1

禁用字节码验证

java.lang.VerifyError - Runtime Unchecked Exception 一旦类文件被加载到Websphere JVM中,接下来就是字节码校验。在字节码校验过程中,如果我们的类违反了JVM约束,就会出现这个错误。

禁用字节码验证。去

管理控制台->server1 ->java 和进程管理->process definition->JVM 参数`

并在 JVM 参数中传递以下字符串

 -Xverify:none 

并在工作区中打开 ApplicationDeploymentDescriptor xml 文件,转到部署选项卡,选择 PARENT_LAST 进行战争,以及第一个选项。这将停止 xml 验证错误。

于 2015-04-11T11:18:50.580 回答
-2

如果我们更改为“先加载父类加载器”,我们看不到异常。这与预期的行为背道而驰。

是的,这是正确的,这是您看到这种行为的唯一方法。我可以建议你看看“你真的得到类加载器吗?” 说话,因为您的问题没有单一或简短的答案。

http://www.slideshare.net/guestd56374/do-you-really-get-class-loaders http://www.parleys.com/#sl=2&st=5&id=1585

于 2010-05-19T07:30:55.560 回答