Java 라이브러리를 JAR로 패키징하고 있으며 java.lang.IncompatibleClassChangeError
메소드를 호출하려고 할 때 많은 것을 던지고 있습니다. 이 오류는 무작위로 나타납니다. 이 오류의 원인은 무엇입니까?
Java 라이브러리를 JAR로 패키징하고 있으며 java.lang.IncompatibleClassChangeError
메소드를 호출하려고 할 때 많은 것을 던지고 있습니다. 이 오류는 무작위로 나타납니다. 이 오류의 원인은 무엇입니까?
답변:
이것은 클라이언트 코드를 다시 컴파일하지 않고 라이브러리에 대해 호환되지 않는 바이너리 변경을 수행했음을 의미합니다. Java Language Specification §13 은 이러한 모든 변경 사항을 가장 두드러지게 설명하며, 비 static
개인 필드 / 방법을 변경 static
하거나 그 반대로 변경합니다.
새 라이브러리에 대해 클라이언트 코드를 다시 컴파일하십시오.
업데이트 : 공용 라이브러리를 게시하는 경우 "이진 역 호환성"이라는 것을 유지하기 위해 호환되지 않는 이진 변경을 최대한 피해야합니다. 의존성 jar 만 업데이트하는 것이 이상적으로 응용 프로그램이나 빌드를 중단해서는 안됩니다. 이진 역 호환성을 중단 해야하는 경우 변경 사항을 릴리스하기 전에 주 버전 번호 (예 : 1.xy에서 2.0.0으로)를 늘리는 것이 좋습니다 .
*.class
파일 삭제 ) 및 재 컴파일 을 시도 했습니까 ? 파일을 편집하면 비슷한 효과가 있습니다.
새로 패키지 된 라이브러리 는 이전 버전의 BC ( Backward Binary Compatible)가 아닙니다 . 이러한 이유로 재 컴파일되지 않은 라이브러리 클라이언트 중 일부는 예외를 발생시킬 수 있습니다.
이전 버전의 라이브러리로 빌드 한 클라이언트가 java.lang을 발생시킬 수있는 Java 라이브러리 API 의 전체 변경 사항 목록입니다. 그들이 새로운 것으로 실행될 때 (즉, BC를 깨는 경우) IncompatibleClassChangeError :
참고 : 다른 호환되지 않는 변경으로 인해 NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError 및 AbstractMethodError 등의 다른 많은 예외 가 있습니다 .
BC에 대한 더 좋은 논문은 Jim des Rivières가 작성한 "Evaving Java-based APIs 2 : Achieving API Binary Compatibility" 입니다.
이러한 변경 사항을 감지 하는 자동 도구 도 있습니다 .
라이브러리에 대한 japi 준수 검사기 사용법 :
japi-compliance-checker OLD.jar NEW.jar
clirr 도구의 사용법 :
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
행운을 빕니다!
이 답변이 모두 맞지만 문제를 해결하는 것이 더 어려운 경우가 많습니다. 일반적으로 클래스 경로에 대한 동일한 종속성의 두 가지 약간 다른 버전의 결과이며 거의 항상 클래스 경로에 대해 원래 컴파일 된 것과 다른 수퍼 클래스 또는 전 이적 클로저의 가져 오기가 다르지만 일반적으로 클래스에서 발생합니다. 인스턴스화 및 생성자 호출. (클래스 로딩 및 ctor 호출이 성공하면 얻을 수 NoSuchMethodException
있습니다.)
동작이 무작위로 나타날 경우, 어떤 코드가 먼저 적중되었는지에 따라 다른 전이 종속성을로드하는 멀티 스레드 프로그램의 결과 일 수 있습니다.
이를 해결하려면 -verbose
인수로 VM을 시작한 다음 예외 발생시로드 된 클래스를 확인하십시오. 당신은 놀라운 정보를 보게 될 것입니다. 예를 들어, 동일한 종속성 및 버전의 사본이 여러 개 있으면 예상치 않았거나 포함 된 것으로 알고 있으면 수락했을 것입니다.
Maven으로 중복 jar를 해결하는 것은 Maven (또는 SBT의 Dependency Graph Plugin ) 에서 maven-dependency-plugin 과 maven-enforcer-plugin을 조합 한 다음 해당 jar을 최상위 POM의 섹션에 추가하거나 가져온 종속성으로 수행하는 것이 가장 좋습니다. SBT의 요소 (종속성을 제거하기 위해).
행운을 빕니다!
또한 JNI를 사용할 때 C ++에서 Java 메소드를 호출 할 때 호출 된 Java 메소드에 잘못된 순서로 매개 변수를 전달하면 호출 된 메소드 내부의 매개 변수를 사용하려고 할 때이 오류가 발생한다는 것을 발견했습니다 (왜냐하면 올바른 유형이 아닙니다). 처음에는 JNI가 메소드를 호출 할 때 클래스 서명 검사의 일부로이 검사를 수행하지 않는다는 사실에 대해 반박했습니다. 그러나 다형성 매개 변수를 전달할 수 있기 때문에 이러한 종류의 검사를 수행하지 않는다고 가정합니다. 당신이하고있는 일을 알고 있다고 가정하십시오.
C ++ JNI 코드 예 :
void invokeFooDoSomething() {
jobject javaFred = FredFactory::getFred(); // Get a Fred jobject
jobject javaFoo = FooFactory::getFoo(); // Get a Foo jobject
jobject javaBar = FooFactory::getBar(); // Get a Bar jobject
jmethodID methodID = getDoSomethingMethodId() // Get the JNI Method ID
jniEnv->CallVoidMethod(javaFoo,
methodID,
javaFred, // Woops! I switched the Fred and Bar parameters!
javaBar);
// << Insert error handling code here to discover the JNI Exception >>
// ... This is where the IncompatibleClassChangeError will show up.
}
Java 코드 예 :
class Bar { ... }
class Fred {
public int size() { ... }
}
class Foo {
public void doSomething(Fred aFred, Bar anotherObject) {
if (name.size() > 0) { // Will throw a cryptic java.lang.IncompatibleClassChangeError
// Do some stuff...
}
}
}
나는 같은 문제가 있었고 나중에 응용 프로그램이 버전 6에서 컴파일되는 동안 Java 버전 1.4에서 응용 프로그램을 실행하고 있음을 알았습니다.
실제로, 그 이유는 중복 라이브러리가 있었기 때문입니다. 하나는 클래스 경로 내에 있고 다른 하나는 클래스 경로 내에있는 jar 파일에 포함되어 있습니다.
이 오류가 나타날 수있는 또 다른 상황은 Emma Code Coverage입니다.
이것은 객체를 인터페이스에 할당 할 때 발생합니다. 나는 이것이 계측되는 객체와 관련이 있고 더 이상 바이너리와 호환되지 않는다고 생각합니다.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
다행히이 문제는 Cobertura에서 발생하지 않으므로 pom.xml의보고 플러그인에 cobertura-maven-plugin을 추가했습니다.
로컬 컴퓨터의 바람둥이 (8.0.20)에 완벽하게 배포되는 웹 응용 프로그램이 있습니다. 그러나 qa 환경 (tomcat-8.0.20)에 넣을 때 IncompatibleClassChangeError가 계속 발생하여 인터페이스에서 확장되고 있다고 불평했습니다. 이 인터페이스는 추상 클래스로 변경되었습니다. 그리고 부모와 자식 클래스를 편집했지만 여전히 같은 문제가 계속 발생했습니다. 마지막으로 디버깅을 원했기 때문에 부모의 버전을 x.0.1-SNAPSHOT으로 변경 한 다음 모든 것을 컴파일하면 작동합니다. 여기에 제공된 답변을 따른 후에도 여전히 문제가 발생하는 경우 pom.xml의 버전도 올바른지 확인하십시오. 작동하는지 확인하려면 버전을 변경하십시오. 그렇다면 버전 문제를 해결하십시오.
내 대답은 Intellij에 특정한 것입니다.
"out"및 "target"디렉토리를 수동으로 삭제하는 경우에도 깔끔하게 재구성했습니다. Intellij에는 "캐시 무효화 및 다시 시작"이있어 때때로 이상한 오류가 해결됩니다. 이번에는 작동하지 않았습니다. 프로젝트 버전-> 모듈 메뉴에서 종속성 버전이 모두 올바르게 보입니다.
마지막 대답은 로컬 maven 저장소에서 문제 종속성을 수동으로 삭제하는 것이 었습니다. bouncycastle의 오래된 버전은 범인이었습니다 (내가 방금 버전을 변경했으며 문제가 될 것임을 알고 있음). 이전 버전은 빌드중인 곳에서 아무 것도 나타나지 않았지만 내 문제를 해결했습니다. 나는 intellij 버전 14를 사용하고 있었고이 과정에서 15로 업그레이드했습니다.
내 경우에는이 방법 으로이 오류가 발생했습니다. pom.xml
내 프로젝트의 두 종속성을 정의 A
하고 B
. 그리고 모두 A
와 B
같은 유물 (호출에 정의 된 종속 C
)하지만 (다른 버전 C.1
과 C.2
). 이런 일이 발생하면 C
maven의 각 클래스에 대해 두 버전에서 하나의 클래스 버전 만 선택할 수 있습니다 ( uber-jar 빌드 중 ). 의존성 중개 규칙에 따라 "가장 가까운"버전을 선택하고 " 우리는 중복 클래스가 있습니다 ..." 라는 경고를 출력합니다 . 버전간에 메소드 / 클래스 서명이 변경 java.lang.IncompatibleClassChangeError
되면 잘못된 버전이있는 경우 예외 가 발생할 수 있습니다. 런타임에 사용됩니다.
고급 : 만약이 A
의 v1을 사용해야 C
과 B
의 v2를 사용해야합니다 C
우리가해야 재배치 C
에 A
와 B
모두에 따라 최종 프로젝트를 빌드 할 때 피할 클래스 충돌의 리딩은 (우리가 중복 클래스 경고가) A
와 B
.
제 경우에는에 com.nimbusds
배포 된 응용 프로그램 에서 라이브러리를 추가 할 때 오류가 발생했습니다 Websphere 8.5
.
아래 예외가 발생했습니다.
원인 : java.lang.IncompatibleClassChangeError : org.objectweb.asm.AnnotationVisitor
해결책은 라이브러리에서 asm jar을 제외하는 것입니다.
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>5.1</version>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
이것이이 오류가 발생할 수있는 기록 인 경우 :
BeanInstantiationException이 CXF ExtensionException을 롤업하여 IncompatibleClassChangeError를 롤업 한 스프링 (3.1.1_release) 구성의 CXF (2.6.0)로드 중 WAS (8.5.0.1)에서이 오류가 발생했습니다. 다음 스 니펫은 스택 추적의 요점을 보여줍니다.
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.cxf.bus.spring.SpringBus]: Constructor threw exception; nested exception is org.apache.cxf.bus.extension.ExtensionException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
... 116 more
Caused by: org.apache.cxf.bus.extension.ExtensionException
at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:167)
at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:179)
at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:138)
at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:131)
[etc...]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
... 118 more
Caused by: java.lang.IncompatibleClassChangeError:
org.apache.neethi.AssertionBuilderFactory
at java.lang.ClassLoader.defineClassImpl(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
[etc...]
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:586)
at java.lang.ClassLoader.loadClass(ClassLoader.java:658)
at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:163)
... 128 more
이 경우 해결책은 내 war 파일에서 모듈의 클래스 경로 순서를 변경하는 것이 었습니다. 즉, WAS 콘솔에서 war 애플리케이션을 열고 클라이언트 모듈을 선택하십시오. 모듈 구성에서 클래스 로딩을 "부모 마지막"으로 설정하십시오.
이것은 WAS 콘솔에서 찾을 수 있습니다.
어떤 이유로 JNI를 사용하고을 호출 할 때 jobject 대신 jclass 인수를 전달할 때도 동일한 예외가 발생합니다 Call*Method()
.
이것은 Ogre Psalm33의 답변과 비슷합니다.
void example(JNIEnv *env, jobject inJavaList) {
jclass class_List = env->FindClass("java/util/List");
jmethodID method_size = env->GetMethodID(class_List, "size", "()I");
long size = env->CallIntMethod(class_List, method_size); // should be passing 'inJavaList' instead of 'class_List'
std::cout << "LIST SIZE " << size << std::endl;
}
나는 5 년 후에이 질문에 대답하는 것이 약간 늦었다는 것을 알고 있지만 이것은 검색 할 때 최고의 히트 중 하나이므로이 java.lang.IncompatibleClassChangeError
특별한 경우를 문서화하고 싶었다.
이 문제의 또 다른 원인은 Instant Run
Android Studio를 사용하도록 설정 한 경우 입니다.
이 오류가 발생하면를 끄십시오 Instant Run
.
Instant Run
개발하는 동안 많은 것들을 수정하여 실행중인 앱에 더 빨리 업데이트를 제공합니다. 따라서 즉시 실행됩니다. 작동하면 정말 유용합니다. 그러나 이와 같은 문제가 발생 Instant Run
하면 다음 버전의 Android Studio가 출시 될 때까지 끄는 것이 가장 좋습니다 .