代码之家  ›  专栏  ›  技术社区  ›  Urs Reupke

如何处理Java中的链接错误?

  •  53
  • Urs Reupke  · 技术社区  · 16 年前

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

    我的应用程序,使用 Java Plugin Framework ,似乎无法转换 dom4j -已将XML文档创建到 Batik's 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系统上。

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

    5 回复  |  直到 8 年前
        1
  •  57
  •   Alex Miller    16 年前

    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实例不同的类加载器加载的。

    你会看到蜡染的版本似乎是从Java插件加载的。而您的服务器正从“”加载,这很可能是内置的JVM加载程序之一(引导类路径、ESOM或类路径)。

    三个突出的类加载器模型是:

    • 委派(JDK中的默认值-询问父级,然后是我)
    • 发布委派(在插件、servlet和需要隔离的地方很常见-先问我,然后问父级)
    • 兄弟(在OSGi、Eclipse等依赖模型中常见)

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

        2
  •  15
  •   matt b    16 年前

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

    javahome/lib-作为根目录
    appserver/lib-作为根目录的子目录
    webapp/web-inf/lib-作为根目录的子目录

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

    在Web环境中,您可能会遇到此层次结构的问题。例如,我以前遇到的一个错误/问题是WEB-INF/LIB中的类依赖于APPServer/LIB中部署的类,而APPServer/LIB又依赖于WEB-INF/LIB中部署的类。这导致了失败,因为当类加载器能够委托给父类加载器时,它们不能委托回树。因此,WEB-INF/lib类加载器将向appserver/lib类加载器请求类,appserver/lib类加载器将加载该类并尝试加载依赖类,但由于在appserver/lib或javahome/lib中找不到该类,因此失败。

    因此,虽然您可能没有在Web/AppServer环境中部署您的应用程序,但如果您的环境设置了类加载器层次结构,我的过长解释可能适用于您。是吗?JPF是否在做一些类加载器魔术,以便能够实现它的插件功能?

        3
  •  6
  •   Buhake Sindi Tesnep    8 年前

    也许这能帮助别人,因为它对我很好。这个问题可以通过集成您自己的依赖关系来解决。遵循以下简单步骤

    首先检查错误如下:

    • 方法执行失败:
    • java.lang.LinkageError:加载程序约束冲突:
    • 当解析方法“org.slf4j.impl. 静态日志绑定器 .getloggerfactory()lorg/slf4j/iloggerfactory;。
    • 当前类的类加载器(org/openmrs/module/module class loader的实例),org/slf4j/ 记录仪厂 ,
    • 以及解析类的类加载器(org/apache/catalina/loader/webappclassloader的实例),org/slf4j/impl/ 静态日志绑定器 ,
    • 对于类型taticBlockerBinder.getLoggerFactory()lorg/slf4j/iloggerFactory,具有不同的类对象;用于签名

    1. 请参见两个突出显示的类。谷歌搜索它们,比如“staticloggerbinder.class jar下载”&“loggerafctory.class jar下载”。这将显示第一个或在某些情况下第二个链接(站点是 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

        4
  •  5
  •   Adam Crume    16 年前

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

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

    我不熟悉Java插件框架,但是我编写了Eclipse的代码,我时常遇到类似的问题。我不保证它能修好,但可能值得一试。

        5
  •  3
  •   Anselm Schuster    14 年前

    亚历克斯和马特的回答很有帮助。我也可以从他们的分析中获益。

    在netbeans RCP框架中使用batik库时,我也遇到了同样的问题,batik库作为“库包装模块”包含在其中。如果其他一些模块使用了XML API,并且该模块不需要依赖BATIK,那么类加载器约束冲突问题会出现类似的错误消息。

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

    我可以通过省略batik库包中的xml apis jar文件来解决这个问题。