在开发一个高度基于 XML 的 Java 应用程序时,我最近在 Ubuntu Linux 上遇到了一个有趣的问题。

我的应用程序,使用 Java插件框架, ,似乎无法转换 dom4j- 创建 XML 文档 蜡染的 SVG 规范的实现。

在控制台上,我得知发生了错误:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature
    at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)
    at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361)
    at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)

我认为该问题是由 JVM 的原始类加载器与插件框架部署的类加载器之间的冲突引起的。

据我所知,不可能指定框架使用的类加载器。也许可以破解它,但我更喜欢采用不太激进的方法来解决这个问题,因为(无论出于何种原因)它只发生在 Linux 系统上。

你们中是否有人遇到过这样的问题,并且知道如何解决它或至少找到问题的核心?

有帮助吗?

解决方案

LinkageError 是一种经典情况,其中您有一个由多个类加载器加载的类 C,并且这些类在同一代码中一起使用(比较、强制转换等),您会遇到这种情况。如果它是相同的类名,或者即使它是从相同的 jar 加载的,也没关系 - 如果从另一个类加载器加载,来自一个类加载器的类始终被视为不同的类。

该消息(多年来已经有了很大改进)说:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: 
loader constraint violation in interface itable initialization: 
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" 
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) 
of the current class, org/apache/batik/dom/svg/SVGOMDocument, 
and the class loader (instance of ) for interface org/w3c/dom/Document 
have different Class objects for the type org/w3c/dom/Attr used in the signature

因此,这里的问题在于解决 SVGOMDocument.createAttribute() 方法,该方法使用 org.w3c.dom.Attr (标准 DOM 库的一部分)。但是,使用 Batik 加载的 Attr 版本是从与传递给该方法的 Attr 实例不同的类加载器加载的。

您会看到 Batik 的版本似乎是从 Java 插件加载的。您的加载器是从“”加载的,它很可能是内置 JVM 加载器之一(引导类路径、ESOM 或类路径)。

三个著名的类加载器模型是:

  • 委托(JDK 中的默认设置 - 先询问父母,然后询问我)
  • 委托后(常见于插件、servlet 和您想要隔离的地方 - 先问我,然后问家长)
  • 兄弟(常见于 OSGi、Eclipse 等依赖模型中)

我不知道 JPF 类加载器使用什么委托策略,但关键是您希望加载一个版本的 dom 库,并且每个人都从同一位置获取该类。这可能意味着将其从类路径中删除并作为插件加载,或者阻止 Batik 加载它,或者其他什么。

其他提示

听起来像是一个类加载器层次结构问题。我不知道你的应用程序部署在什么类型的环境中,但有时这个问题可能发生在Web环境中 - 应用程序服务器创建类加载器的层次结构,类似于:

javahome / lib - 作为root
appserver / lib - 作为root的孩子 webapp / WEB-INF / lib - 作为root的孩子的孩子 等

通常类加载器将加载委托给它们的父类加载器(这称为“ parent-first ”),如果该类加载器找不到该类,则子类加载器会尝试。例如,如果在webapp / WEB-INF / lib中部署为JAR的类尝试加载一个类,首先它会要求appserver / lib对应的类加载器加载该类(这反过来要求对应于javahome / lib的类加载器)加载类),如果此查找失败,则搜索WEB-INF / lib以查找此类的匹配项。

在Web环境中,您可能会遇到此层次结构的问题。例如,我之前遇到的一个错误/问题是WEB-INF / lib中的一个类依赖于appserver / lib中部署的类,而后者依赖于部署在WEB-INF / lib中的类。这导致失败,因为虽然类加载器能够委托给父类加载器,但它们不能委托回树。因此,WEB-INF / lib类加载器会向appserver / lib类加载器询问一个类,appserver / lib类加载器会加载该类并尝试加载依赖类,并失败,因为它无法在appserver / lib或javahome中找到该类/ lib中。

因此,虽然您可能没有在Web / app服务器环境中部署应用程序,但如果您的环境具有设置的类加载器层次结构,那么我太长的解释可能适用于您。可以? JPF是否能够实现某种类加载器魔法以实现它的插件功能?

也许这会对某人有所帮助,因为它对我来说非常好。该问题可以通过集成您自己的依赖项来解决。按照这个简单的步骤操作

首先检查错误应该是这样的:

  • 方法执行失败:
  • java.lang.LinkageError:违反加载器约束:
  • 当解析方法“org.slf4j.impl.静态记录器绑定器.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
  • 当前类 org/slf4j/ 的类加载器(org/openmrs/module/ModuleClassLoader 的实例)记录器工厂,
  • 以及解析类 org/slf4j/impl/ 的类加载器(org/apache/catalina/loader/WebappClassLoader 的实例)静态记录器绑定器,
  • 对于类型 taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory 有不同的 Class 对象;在签名中使用

  1. 请参阅两个突出显示的类。Google 搜索它们,例如“StaticLoggerBinder.class jar download”和“LoggeraFactory.class jar download”。这将向您显示第一个或在某些情况下第二个链接(网站是 http://www.java2s.com )这是您已包含在项目中的 jar 版本之一。你可以自己聪明地识别它,但我们沉迷于谷歌;)

  2. 之后你就会知道 jar 文件名,在我的例子中它就像 slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8

  3. 现在可以在此处找到该文件的最新版本 http://mvnrepository.com/ (实际上到目前为止的所有版本,这是 Maven 获取依赖项的站点)。
  4. 现在将两个文件添加为最新版本的依赖项(或保持两个文件版本相同,选择的版本都是旧版本)。以下是您必须包含在 pom.xml 中的依赖项

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.7</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.7</version>
</dependency>

How to get dependecy definition from Maven Site

你能指定一个类加载器吗?如果没有,请尝试指定上下文类加载器,如下所示:

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

我不熟悉Java插件框架,但是我为Eclipse编写代码,并且我不时遇到类似的问题。我不保证它会解决它,但它可能值得一试。

亚历克斯和马特的答案非常有帮助。我也可以从他们的分析中受益。

在Netbeans RCP框架中使用Batik库时遇到了同样的问题,Batik库被包含为“Library Wrapper Module”。如果某个其他模块使用XML apis,并且不需要为该模块建立对Batik的依赖,则类似的错误消息会出现类加载器约束违规问题。

在Netbeans中,各个模块使用专用的类加载器,模块之间的依赖关系意味着合适的类加载器委派路由。

我可以通过简单地从Batik库包中省略xml-apis jar文件来解决问题。

此问题所述,启用 -verbose:class 将使JVM记录有关正在加载的所有类的日志信息,这对于理解类在更复杂的场景中的来源非常有帮助。应用

你得到的输出看起来大致相同(从那个问题复制):

[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top