Я пакую бібліотеку Java як JAR, і вона кидає багато java.lang.IncompatibleClassChangeError
с, коли я намагаюся викликати методи з неї. Ці помилки здаються випадковими. Які проблеми можуть спричинити цю помилку?
Я пакую бібліотеку Java як JAR, і вона кидає багато java.lang.IncompatibleClassChangeError
с, коли я намагаюся викликати методи з неї. Ці помилки здаються випадковими. Які проблеми можуть спричинити цю помилку?
Відповіді:
Це означає, що ви внесли деякі несумісні бінарні зміни в бібліотеку, не перекомпілюючи код клієнта. Спеціалізація мови 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 - це "Еволюціонуючі Java-API 2: Досягнення бінарної сумісності API", написаний Джим де Рів'єром.
Існують також деякі автоматичні інструменти для виявлення таких змін:
Використання перевірки відповідності japi для вашої бібліотеки:
japi-compliance-checker OLD.jar NEW.jar
Використання інструменту clirr:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
Удачі!
Хоча ці відповіді правильні, вирішити проблему часто складніше. Це, як правило, результат двох м'яко різних версій однієї і тієї ж залежності від classpath, і майже завжди викликається або іншим надкласом, ніж спочатку був складений проти того, щоб бути на classpath, або якийсь імпорт перехідного закриття відрізнявся, але, як правило, у класі інстанція та виклик конструктора. (Після успішного завантаження класу та виклику ctor ви отримаєте NoSuchMethodException
чи що.)
Якщо поведінка виявляється випадковою, ймовірно, це результат багатопотокової програми, що завантажує різні перехідні залежності, залежно від того, який код потрапив першим.
Щоб вирішити ці проблеми, спробуйте запустити VM -verbose
в якості аргументу, а потім подивіться класи, які завантажувались, коли відбувається виняток. Ви повинні побачити деяку дивовижну інформацію. Наприклад, маючи кілька копій однієї і тієї ж залежності та версії, яких ви ніколи не очікували або прийняли б, якби знали, що вони включаються.
Розв’язування дублікатів баночок за допомогою Maven найкраще проводити за допомогою комбінації плагіну Maven-залежності та maven-Execucer-плагіну під Maven (або плагіну графіку залежності залежності від SBT , потім додаючи ці банки до розділу вашого 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.
Власне, причина полягала в тому, що бібліотека-дублікат була одна, одна розташована у класі class, а інша - всередині файлу jar, який знаходиться в межах classpath.
Інша ситуація, коли ця помилка може з’явитися, - це з покриттям коду Emma.
Це відбувається під час присвоєння Об'єкту інтерфейсу. Я думаю, це має щось спільне з тим, що Об'єкт інструментується і вже не є бінарним сумісним.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
На щастя, цієї проблеми не виникає з Cobertura, тому я додав плагін cobertura-maven у свої плагіни для звітування мого pom.xml
Я стикався з цим питанням під час нерозгортання та перерозподілу війни зі скляними рибками. Моя структура класу була такою,
public interface A{
}
public class AImpl implements A{
}
і це було змінено на
public abstract class A{
}
public class AImpl extends A{
}
Після зупинки та перезавантаження домену він вийшов чудово. Я використовував рибку 3.1.43
У мене є веб-додаток, який прекрасно розгортається на tomcat моєї локальної машини (8.0.20). Однак, коли я помістив його в середовище qa (tomcat - 8.0.20), він продовжував надавати мені IncompatibleClassChangeError, і він скаржився, що я поширююсь на інтерфейс. Цей інтерфейс було змінено на абстрактний клас. І я склав уроки для батьків та дітей, і все ще продовжував отримувати те саме питання. Нарешті, я хотів налагоджувати помилку, тому я змінив версію на батьківській програмі на x.0.1-SNAPSHOT, а потім скомпілював усе і зараз це працює. Якщо хтось все-таки відчуває проблему після виконання відповідей, наведених тут, переконайтесь, що версії вашого pom.xml також є правильними. Змініть версії, щоб побачити, чи це працює. Якщо так, то виправте проблему з версією.
Я вважаю, що моя відповідь буде конкретна Intellij.
Я був відновлений в чистоті, навіть забираючи вручну видалення "назовні" та "цільових" брудів. 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
must use v2 of C
, тоді ми повинні переїхати C
в A
і B
'poms, щоб уникнути конфлікту класів (у нас є дублювання попередження про клас) при складанні остаточного проекту, який залежить і від, A
і від B
.
У моєму випадку помилка з’явилася, коли я додав com.nimbusds
бібліотеку у свою програму, розгорнуту в Websphere 8.5
.
Виник нижче:
Викликано: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
Рішення полягало в тому, щоб виключити банку з посиланням з бібліотеки:
<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), під час завантаження пружини (3.1.1_release) CXF (2.6.0), де 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
У цьому випадку рішенням було змінити порядок курсу модуля в моєму файлі війни. Тобто, відкрийте програму війни на консолі WAS під та виберіть клієнтський модуль. У конфігурації модуля встановіть завантаження класів "батьківським останнім".
Це знайдено в консолі WAS:
Документування іншого сценарію після спалювання занадто багато часу.
Переконайтеся, що у вас немає банку залежностей, на якому є клас із приміткою EJB.
У нас був спільний файл з баночкою, який мав @local
примітку. Пізніше цей клас був перенесений із цього спільного проекту та до нашого основного проекту банку jar. Наша банка ejb і наша звичайна банка обидва є в вусі. Не було оновлено версію нашої загальної залежності від банок. Таким чином, 2 класи намагаються бути чимось із несумісними змінами.
Чомусь той самий виняток також кидається при використанні JNI та передачі аргументу jclass замість завдання для виклику a 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.