Я упаковываю библиотеку Java как JAR, и она выдает много java.lang.IncompatibleClassChangeError
s, когда я пытаюсь вызвать из нее методы. Эти ошибки кажутся случайными. Какие проблемы могут быть причиной этой ошибки?
Я упаковываю библиотеку Java как JAR, и она выдает много java.lang.IncompatibleClassChangeError
s, когда я пытаюсь вызвать из нее методы. Эти ошибки кажутся случайными. Какие проблемы могут быть причиной этой ошибки?
Ответы:
Это означает, что вы внесли некоторые несовместимые двоичные изменения в библиотеку, не перекомпилировав код клиента. Спецификация языка Java § 13 подробно описывает все такие изменения, в первую очередь, изменение не- static
не приватных полей / методов на static
или наоборот.
Перекомпилируйте клиентский код с новой библиотекой, и вы должны быть в порядке.
ОБНОВЛЕНИЕ: Если вы публикуете публичную библиотеку, вам следует избегать как можно большего количества несовместимых двоичных изменений, чтобы сохранить так называемую «двоичную обратную совместимость». Обновление банок зависимостей в идеале не должно нарушать работу приложения или сборки. Если вам необходимо нарушить двоичную обратную совместимость, рекомендуется увеличить номер основной версии (например, с 1.xy до 2.0.0) перед тем, как отменить изменение.
*.class
файлы) и перекомпилировать? Редактирование файлов имеет аналогичный эффект.
Ваша недавно упакованная библиотека не имеет обратной совместимости с двоичными файлами (BC) со старой версией. По этой причине некоторые из библиотечных клиентов, которые не перекомпилированы, могут выдать исключение.
Это полный список изменений в API библиотеки Java, которые могут привести к тому, что клиенты, созданные со старой версией библиотеки, сгенерируют java.lang. IncompatibleClassChangeError, если они запускаются на новом (то есть, ломая BC):
Примечание . Существует множество других исключений, вызванных другими несовместимыми изменениями: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError и AbstractMethodError .
Лучшая статья о BC - «Развитие API на основе Java 2: достижение двоичной совместимости API», написанная Jim des Rivières.
Есть также несколько автоматических инструментов для обнаружения таких изменений:
Использование japi-Compliance-Checker для вашей библиотеки:
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
в качестве аргумента, а затем посмотрите на классы, которые загружались при возникновении исключения. Вы должны увидеть некоторую удивительную информацию. Например, наличие нескольких копий одной и той же зависимости и версий, которые вы никогда не ожидали или приняли бы, если бы знали, что они включены.
Устранение дубликатов jar-файлов с помощью Maven лучше всего выполнить с помощью комбинации плагинов maven-dependency-plugin и maven-inspecer-plugin в Maven (или подключаемого модуля SBT Dependency Graph , с последующим добавлением этих jar-файлов в раздел POM верхнего уровня или в качестве импортированной зависимости). элементы в SBT (чтобы удалить эти зависимости).
Удачи!
Я также обнаружил, что при использовании JNI, при вызове метода Java из C ++, если вы передадите параметры вызванному методу 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...
}
}
}
У меня была та же проблема, и позже я понял, что я запускаю приложение на Java версии 1.4, в то время как приложение скомпилировано на версии 6.
На самом деле причина была в том, что у нас была дублирующаяся библиотека: одна находится в пути к классам, а другая включена в файл JAR, расположенный в пути к классам.
Еще одна ситуация, в которой может появиться эта ошибка, - Эмма-код Покрытие.
Это происходит при назначении объекта интерфейсу. Я предполагаю, что это как-то связано с инструментируемым объектом, который больше не является бинарно-совместимым.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
К счастью, эта проблема не возникает с Cobertura, поэтому я добавил cobertura-maven-plugin в свои плагины для отчетов моего pom.xml
Я столкнулся с этой проблемой при развертывании и развертывании войны со стеклянной рыбой. Моя классовая структура была такой,
public interface A{
}
public class AImpl implements A{
}
и это было изменено на
public abstract class A{
}
public class AImpl extends A{
}
После остановки и перезапуска домена все заработало нормально. Я использовал Glassfish 3.1.43
У меня есть веб-приложение, которое прекрасно разворачивается на tomcat моей локальной машины (8.0.20). Однако, когда я поместил его в среду qa (tomcat - 8.0.20), он продолжал давать мне IncompatibleClassChangeError, и он жаловался, что я расширяю интерфейс. Этот интерфейс был изменен на абстрактный класс. И я скомпилировал родительские и дочерние классы, и все же продолжал получать ту же проблему. Наконец, я хотел отладить, поэтому я изменил версию на родительском для x.0.1-SNAPSHOT, а затем скомпилировал все, и теперь это работает. Если кто-то все еще сталкивается с проблемой после выполнения ответов, приведенных здесь, убедитесь, что версии в вашем файле pom.xml также верны. Измените версии, чтобы увидеть, работает ли это. Если так, то исправьте проблему с версией.
Мой ответ, я полагаю, будет конкретным для Интеллидж.
Я восстановил чистый, даже зашел так далеко, что вручную удалил "out" и "target" dirs. 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 of C
и B
должно использовать v2 of C
, то мы должны переместить C
в A
и B
poms, чтобы избежать конфликта классов (у нас есть дублирующееся предупреждение о классе) при построении окончательного проекта, который зависит от обоих 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>
Если это запись о возможных случаях этой ошибки, то:
Я только что получил эту ошибку на WAS (8.5.0.1), во время загрузки CXF (2.6.0) конфигурации Spring (3.1.1_release), где BeanInstantiationException свернуло исключение CXF ExtensionException и свернуло IncompatibleClassChangeError. Следующий фрагмент кода показывает суть трассировки стека:
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 и выберите клиентские модули. В конфигурации модуля установите загрузку классов как «родительский последний».
Это находится в консоли WAS:
Документирование другого сценария после прожигания слишком много времени.
Убедитесь, что у вас нет jar зависимостей, в котором есть класс с аннотацией EJB.
У нас был общий jar-файл с @local
аннотацией. Позже этот класс был перенесен из этого общего проекта в наш основной проект ejb jar. Наш ejb jar и наш общий jar связаны в ухо. Версия нашей общей jar-зависимости не была обновлена. Таким образом 2 класса пытаются быть чем-то с несовместимыми изменениями.
По какой-то причине то же исключение также выдается при использовании JNI и передаче аргумента jclass вместо jobject при вызове Call*Method()
.
Это похоже на ответ из Огрского псалма 33.
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
поэтому я хотел задокументировать этот особый случай.
Добавление моих 2 центов. Если вы используете scala и sbt и scala-logging в качестве зависимости, то это может произойти, потому что более ранняя версия scala-logging имела имя scala-logging-api.So; по сути, разрешения зависимостей не происходят из-за различных имена, приводящие к ошибкам во время выполнения приложения scala.
Дополнительная причина этой проблемы - если вы Instant Run
включили Android Studio.
Если вы обнаружите, что начинаете получать эту ошибку, выключите Instant Run
.
Instant Run
изменяет большое количество вещей во время разработки, чтобы быстрее предоставлять обновления для вашего работающего приложения. Отсюда мгновенный бег. Когда это работает, это действительно полезно. Однако, когда возникает такая проблема, лучше всего отключить ее Instant Run
до следующей версии Android Studio.