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

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

我的应用程序使用Java插件框架 ,似乎无法将dom4j创build的XML文档转换为Batik的 SVG规范实现。

在控制台上,我知道发生了一个错误:

线程“AWT-EventQueue-0”中的exceptionjava.lang.LinkageError:接口中的加载器约束违规itable初始化:何时parsing方法org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava / lang / String;)Lorg / W3C / DOM /的Attr;”  org / apache / batik / dom / svg / SVGOMDocument的类加载器(org / java / plugin / standard / StandardPluginClassLoader的实例),org / w3c / dom / Document在org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)org.dom4j.io.DOMWriter签名中使用的typesorg / w3c / dom / Attr具有不同的Class对象.createDomDocument(DOMWriter.java:361)at org.dom4j.io.DOMWriter.write(DOMWriter.java:138) 

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

据我所知,不可能为框架指定一个类加载器来使用。 有可能破解它,但是我宁愿采取一些不积极的方法来解决这个问题,因为(无论出于何种原因)它只发生在Linux系统上。

你们有没有遇到过这样的问题,并且有什么想法如何解决这个问题,或者至less能够解决问题的核心问题?

LinkageError是你在一个经典的案例中得到的,在这个案例中你有一个由多个类加载器加载的类C,这些类在相同的代码中被使用(比较,转换等)。 无论它是相同的Class名称,还是从相同的jar中加载都没关系 – 如果从另一个类加载器加载,则来自一个classloader的Class始终被视为不同的Class。

这个信息(这些年来有了很大的改进)说:

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 

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

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

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

  • 代表团(在JDK中的默认 – 请问父母​​,然后我)
  • 后授权(通常在插件,servlet和你想隔离的地方 – 问我,然后是父母)
  • (在OSGi,Eclipse等依赖模型中很常见)

我不知道JPF类加载器使用了什么委派策略,但关键是你想要加载一个dom库版本,并且每个人都从同一个位置获取这个类。 这可能意味着将其从类path中移除并作为插件加载,或阻止蜡染加载或其他。

听起来像一个类加载器的层次结构问题。 我无法确定您的应用程序部署在什么types的环境中,但有时候这个问题可能发生在Web环境中 – 应用程序服务器创build类加载器的层次结构,类似于:

javahome / lib – 作为根
appserver / lib – 作为root的子项
webapp / WEB-INF / lib – 作为根的孩子的小孩
等等

通常,类加载器将加载委托给它们的父类加载器(这被称为“ parent-first ”),如果该类加载器找不到类,那么子类加载器尝试执行。 例如,如果在webapp / WEB-INF / lib中部署为JAR的类尝试加载类,则首先会要求相应于appserver / lib的类加载器加载该类(这又会要求相应于javahome / lib的类加载器加载类),如果这个查找失败,那么searchWEB-INF / lib以匹配这个类。

在Web环境中,您可能遇到此层次结构的问题。 例如,之前遇到的一个错误/问题是,WEB-INF / lib中的类取决于部署在appserver / lib中的类,而该类又取决于在WEB-INF / lib中部署的类。 这导致失败,因为虽然类加载器能够委托给父类加载器,但是它们不能向下代理树。 因此,WEB-INF / lib类加载器会向appserver / lib classloader请求一个类,appserver / lib classloader会加载该类,并尝试加载相关类,并失败,因为它无法在appserver / lib或javahome中find该类/ lib目录下。

因此,虽然您可能不会在Web /应用程序服务器环境中部署您的应用程序,但如果您的环境设置了类加载程序的层次结构,那么我的太长解释可能适用于您。 可以? JPF是否做了一些类加载器的魔术来实现它的插件function?

可能这会帮助别人,因为它对我来说非常好。 这个问题可以通过集成你自己的依赖来解决。 按照这个简单的步骤

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

  • 方法执行失败:
  • java.lang.LinkageError:加载器约束违例:
  • 当parsing方法“org.slf4j.impl。StaticLoggerBinder .getLoggerFactory()Lorg / slf4j / ILoggerFactory;”
  • 当前类org / slf4j / LoggerFactory的类加载器(org / openmrs / module / ModuleClassLoader的实例)
  • 以及parsing类org / slf4j / impl / StaticLoggerBinder的类加载器(org / apache / catalina / loader / WebappClassLoader的实例)
  • 为typestaticLoggerBinder.getLoggerFactory()提供不同的Class对象()Lorg / slf4j / ILoggerFactory; 用于签名

  1. 看到两个突出的类。 谷歌search他们就像“StaticLoggerBinder.class jar下载”&“LoggeraFactory.class jar下载”。 这将首先显示或在某些情况下显示第二个链接(网站是http://www.java2s.com ),这是您的项目中包含的jar版本之一。 你可以巧妙地识别它自己,但我们沉迷于谷歌;)

  2. 之后,你会知道jar文件的名字,在我的情况下,就像slf4j-log4j12-1.5.6.jar&slf4j-api-1.5.8

  3. 现在这个文件的最新版本可以在这里http://mvnrepository.com/ (实际上所有的版本,直到date,这是从哪里maven得到你的依赖的网站)。
  4. 现在将这两个文件作为依赖项添加到最新版本(或者保持两个文件版本相同,或者select版本旧)。 以下是您必须在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> 

如何从Maven站点获得依赖性定义

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

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

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

Alex和Matt的回答非常有帮助。 我也可以从他们的分析中受益。

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

在Netbeans中,单独的模块使用专用的类加载器,并且模块之间的依赖关系意味着合适的类加载器委托路由。

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