program tip

Java에서 LinkageErrors를 처리하는 방법은 무엇입니까?

radiobox 2020. 12. 25. 09:09
반응형

Java에서 LinkageErrors를 처리하는 방법은 무엇입니까?


XML 기반의 Java 응용 프로그램을 개발하면서 최근 Ubuntu Linux에서 흥미로운 문제가 발생했습니다.

Java Plugin Framework를 사용하는 내 애플리케이션 dom4j 생성 XML 문서를 Batik의 SVG 사양 구현으로 변환 할 수없는 것으로 보입니다 .

콘솔에서 오류가 발생했음을 알 수 있습니다.

스레드 "AWT-EventQueue-0"java.lang.LinkageError : 인터페이스 반복 초기화의 로더 제한 조건 위반 : "org.apache.batik.dom.svg.SVGOMDocument.createAttribute (Ljava / lang / String;) Lorg 메소드를 분석 할 때 / w3c / dom / Attr; " 현재 클래스의 클래스 로더 (org / java / plugin / standard / StandardPluginClassLoader의 인스턴스), org / apache / batik / dom / svg / SVGOMDocument 및 인터페이스 org / w3c /에 대한 클래스 로더 (<bootloader>의 인스턴스) dom / Document에는 서명에 사용 된 org / w3c / dom / Attr 유형에 대해 서로 다른 클래스 객체가 있습니다.
    org.apache.batik.dom.svg.SVGDOMImplementation.createDocument (SVGDOMImplementation.java:149)
    org.dom4j.io.DOMWriter.createDomDocument (DOMWriter.java:361)
    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

따라서 여기서 문제는 org.w3c.dom.Attr (표준 DOM 라이브러리의 일부)을 사용하는 SVGOMDocument.createAttribute () 메서드를 해결하는 것입니다. 그러나 Batik으로로드 된 Attr 버전은 메서드에 전달하는 Attr 인스턴스와 다른 클래스 로더에서로드되었습니다.

Batik의 버전이 Java 플러그인에서로드 된 것처럼 보입니다. 그리고 당신은 내장 JVM 로더 (부트 클래스 경로, ESOM 또는 클래스 경로) 중 하나 인 ""에서로드되고 있습니다.

세 가지 눈에 띄는 클래스 로더 모델은 다음과 같습니다.

  • 위임 (JDK의 기본값-부모에게 물어 본 다음 나에게)
  • 사후 위임 (플러그인, 서블릿 및 격리를 원하는 장소에서 일반적으로 사용-저에게 물어 본 다음 부모)
  • 형제 (OSGi, Eclipse 등과 같은 종속성 모델에서 일반적)

JPF 클래스 로더가 어떤 위임 전략을 사용하는지 모르겠지만 핵심은 dom 라이브러리의 한 버전을로드하고 모든 사람이 동일한 위치에서 해당 클래스를 소싱하기를 원한다는 것입니다. 이는 클래스 경로에서 제거하고 플러그인으로로드하거나 Batik이로드하지 못하도록하는 것을 의미 할 수 있습니다.


클래스 로더 계층 문제처럼 들립니다. 응용 프로그램이 어떤 유형의 환경에 배포되었는지 알 수는 없지만 때때로이 문제가 웹 환경에서 발생할 수 있습니다. 응용 프로그램 서버가 다음과 같은 클래스 로더 계층을 생성하는 경우 :

javahome / lib-루트
appserver / lib- 루트의 자식
webapp / WEB-INF / lib-루트의 자식

일반적으로 클래스 로더는 부모 클래스 로더 ( " parent-first"라고 함)에 로드를 위임 하고 해당 클래스 로더가 클래스를 찾을 수없는 경우 자식 클래스 로더가이를 시도합니다. 예를 들어 webapp / WEB-INF / lib에 JAR로 배포 된 클래스가 클래스를로드하려고하면 먼저 appserver / lib에 해당하는 클래스 로더에게 클래스를로드하도록 요청합니다 (이는 다시 javahome / lib에 해당하는 클래스 로더를 요청합니다. 이 검색이 실패하면 WEB-INF / lib에서이 클래스와 일치하는 항목을 검색합니다.

웹 환경에서는이 계층 구조에 문제가 발생할 수 있습니다. 예를 들어 이전에 내가 겪은 한 가지 실수 / 문제는 WEB-INF / lib의 클래스가 appserver / lib에 배포 된 클래스에 의존했을 때였으며, 이는 차례로 WEB-INF / lib에 배포 된 클래스에 의존했습니다. 클래스 로더가 상위 클래스 로더에 위임 할 수있는 동안 트리 아래로 다시 위임 할 수 없기 때문에 이로 인해 실패했습니다. 따라서 WEB-INF / lib 클래스 로더는 클래스에 대해 appserver / lib 클래스 로더를 요청하고 appserver / lib 클래스 로더는 해당 클래스를로드하고 종속 클래스를로드하려고 시도하며 appserver / lib 또는 javahome에서 해당 클래스를 찾을 수 없기 때문에 실패합니다. / lib.

따라서 웹 / 앱 서버 환경에 앱을 배포하지 않을 수 있지만 환경에 클래스 로더 계층이 설정되어있는 경우 너무 긴 설명이 적용될 수 있습니다. 그렇습니까? JPF는 플러그인 기능을 구현할 수 있도록 일종의 클래스 로더 마법을 수행하고 있습니까?


그것은 나를 위해 꽤 잘 작동하기 때문에 누군가를 도울 수 있습니다. 자체 종속성을 통합하여 문제를 해결할 수 있습니다. 이 간단한 단계를 따르십시오

먼저 다음과 같은 오류를 확인하십시오.

  • 메소드 실행 실패 :
  • java.lang.LinkageError : 로더 제약 위반 :
  • "org.slf4j.impl. StaticLoggerBinder .getLoggerFactory () Lorg / slf4j / ILoggerFactory;" 메소드를 해결할 때
  • 현재 클래스 org / slf4j / LoggerFactory 의 클래스 로더 (org / openmrs / module / ModuleClassLoader 인스턴스) ,
  • 해결 된 클래스, org / slf4j / impl / StaticLoggerBinder에 대한 클래스 로더 (org / apache / catalina / loader / WebappClassLoader의 인스턴스) ,
  • taticLoggerBinder.getLoggerFactory () Lorg / slf4j / ILoggerFactory 유형에 대해 다른 클래스 오브젝트를 갖습니다. 서명에 사용

  1. 두 개의 강조 표시된 클래스를 참조하십시오. Google은 "StaticLoggerBinder.class jar 다운로드"및 "LoggeraFactory.class jar 다운로드"와 같이 검색합니다. 그러면 프로젝트에 포함 된 jar 버전 중 하나 인 첫 번째 또는 경우에 따라 두 번째 링크 (사이트는 http://www.java2s.com )가 표시됩니다. 당신은 그것을 스스로 똑똑하게 식별 할 수 있지만, 우리는 구글에 중독되어 있습니다.)

  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 Plugin Framework에 익숙하지 않지만 Eclipse 용 코드를 작성하고 가끔 비슷한 문제가 발생합니다. 나는 그것이 그것을 고칠 것이라고 보장하지는 않지만 아마도 한 번의 가치가 있습니다.


Alex와 Matt의 답변은 매우 유용합니다. 나는 그들의 분석으로부터도 혜택을 볼 수 있습니다.

I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.

In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.

I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.


As specified in this question, enabling the -verbose:class will make the JVM log information about all classes being loaded, which can be incredibly helpful to understand where the classes are coming from in more complex scenarios & applications.

The output you get looks roughly like this (copied from that question):

[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]

ReferenceURL : https://stackoverflow.com/questions/244482/how-to-deal-with-linkageerrors-in-java

반응형