Pakuję bibliotekę Java jako plik JAR i rzuca wiele java.lang.IncompatibleClassChangeError
s, gdy próbuję wywołać z niej metody. Te błędy wydają się pojawiać losowo. Jakie problemy mogą powodować ten błąd?
Pakuję bibliotekę Java jako plik JAR i rzuca wiele java.lang.IncompatibleClassChangeError
s, gdy próbuję wywołać z niej metody. Te błędy wydają się pojawiać losowo. Jakie problemy mogą powodować ten błąd?
Odpowiedzi:
Oznacza to, że dokonałeś pewnych niekompatybilnych zmian binarnych w bibliotece bez ponownej kompilacji kodu klienta. Specyfikacja języka Java §13 wyszczególnia wszystkie takie zmiany, w sposób najbardziej widoczny, zmieniając nieprywatne static
pola / metody na static
lub odwrotnie.
Ponownie skompiluj kod klienta względem nowej biblioteki i powinieneś być gotowy.
AKTUALIZACJA: Jeśli publikujesz bibliotekę publiczną, powinieneś unikać dokonywania możliwie niekompatybilnych zmian binarnych, aby zachować tak zwaną „binarną kompatybilność wsteczną”. Samo aktualizowanie słoików zależności nie powinno uszkodzić aplikacji ani kompilacji. Jeśli musisz przerwać binarną kompatybilność wsteczną, zaleca się zwiększenie głównego numeru wersji (np. Z 1.xy do 2.0.0) przed wydaniem zmiany.
*.class
pliki) i ponownie skompilować? Edytowanie plików ma podobny efekt.
Twoja nowo spakowana biblioteka nie jest wstecznie kompatybilna binarnie (BC) ze starą wersją. Z tego powodu niektórzy klienci biblioteki, którzy nie zostali ponownie skompilowani, mogą zgłosić wyjątek.
Jest to pełna lista zmian w interfejsie API biblioteki Java, które mogą spowodować, że klienci zbudowani ze starej wersji biblioteki wyrzucą java.lang. IncompatibleClassChangeError, jeśli działają na nowym (tzn. Zepsują BC):
Uwaga : Istnieje wiele innych wyjątków spowodowanych innymi niezgodnymi zmianami: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError i AbstractMethodError .
Lepszym tekstem na temat BC jest „Ewolucja API opartych na Javie 2: Osiąganie zgodności binarnej API” napisana przez Jima des Rivières.
Istnieją również automatyczne narzędzia do wykrywania takich zmian:
Zastosowanie sprawdzania zgodności japi dla twojej biblioteki:
japi-compliance-checker OLD.jar NEW.jar
Zastosowanie narzędzia Clirr:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
Powodzenia!
Chociaż wszystkie te odpowiedzi są poprawne, rozwiązanie problemu jest często trudniejsze. Zasadniczo wynika to z dwóch nieznacznie różnych wersji tej samej zależności od ścieżki klasy i prawie zawsze jest spowodowana albo inną nadklasą, niż pierwotnie skompilowano, że nie jest na ścieżce klasy lub inny import zamknięcia przechodniego jest inny, ale ogólnie w klasie tworzenie instancji i wywoływanie konstruktora. (Po udanym załadowaniu klasy i wywołaniu ctor dostaniesz NoSuchMethodException
lub coś takiego .)
Jeśli zachowanie wydaje się losowe, prawdopodobnie jest to wynikiem działania wielowątkowej klasy programu ładującej różne zależności przechodnie na podstawie tego, jaki kod został trafiony jako pierwszy.
Aby rozwiązać ten problem, spróbuj uruchomić maszynę wirtualną z -verbose
argumentem, a następnie spójrz na klasy, które były ładowane, gdy wystąpi wyjątek. Powinieneś zobaczyć zaskakujące informacje. Na przykład posiadanie wielu kopii tej samej zależności i wersji, których nigdy się nie spodziewałeś lub zaakceptowałbyś, gdybyś wiedział, że są one uwzględnione.
Rozwiązanie zduplikowanych słoików za pomocą Maven najlepiej wykonać za pomocą kombinacji wtyczki zależnej od maven i wtyczki maven-enforcer w Maven (lub SBT's Dependency Graph Plugin) , a następnie dodając te słoiki do sekcji POM najwyższego poziomu lub jako zależność importowaną elementy w SBT (aby usunąć te zależności).
Powodzenia!
Odkryłem również, że podczas używania JNI, wywoływania metody Java z C ++, jeśli przekażesz parametry do wywoływanej metody Java w niewłaściwej kolejności, pojawi się ten błąd, gdy spróbujesz użyć parametrów wewnątrz wywoływanej metody (ponieważ nie będzie odpowiednim typem). Początkowo byłem zaskoczony, że JNI nie wykonuje tego sprawdzania w ramach sprawdzania podpisu klasy podczas wywoływania metody, ale zakładam, że nie robią tego rodzaju sprawdzania, ponieważ możesz przekazywać parametry polimorficzne i muszą Załóżmy, że wiesz, co robisz.
Przykładowy kod JNI C ++:
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.
}
Przykładowy kod 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...
}
}
}
Miałem ten sam problem, a później zorientowałem się, że uruchamiam aplikację w Javie w wersji 1.4, podczas gdy aplikacja jest kompilowana w wersji 6.
W rzeczywistości przyczyną było posiadanie zduplikowanej biblioteki, jedna znajduje się w ścieżce klasy, a druga znajduje się w pliku jar, który znajduje się w ścieżce klasy.
Inną sytuacją, w której może pojawić się ten błąd, jest usługa Emma Code Coverage.
Dzieje się tak podczas przypisywania obiektu do interfejsu. Wydaje mi się, że ma to coś wspólnego z instrumentowanym Obiektem i nie jest kompatybilny binarnie.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
Na szczęście ten problem nie występuje w Cobertura, więc dodałem wtyczkę cobertura-maven-plugin do moich wtyczek raportujących mojej pom.xml
Napotkałem ten problem podczas cofania i ponownego wdrażania wojny ze szklistymi rybami. Moja struktura klasowa była taka,
public interface A{
}
public class AImpl implements A{
}
i został zmieniony na
public abstract class A{
}
public class AImpl extends A{
}
Po zatrzymaniu i ponownym uruchomieniu domeny wszystko poszło dobrze. Używałem szklanych ryb 3.1.43
Mam aplikację internetową, która doskonale się wdraża na tomcat mojego komputera lokalnego (8.0.20). Jednak, kiedy umieściłem go w środowisku qa (tomcat - 8.0.20), nadal dawał mi IncompatibleClassChangeError i narzekałem, że rozszerzam interfejs. Ten interfejs został zmieniony na klasę abstrakcyjną. I skompilowałem klasy dla rodziców i dzieci i wciąż otrzymywałem ten sam problem. Wreszcie chciałem debugować, więc zmieniłem wersję nadrzędną na x.0.1-SNAPSHOT, a następnie skompilowałem wszystko i teraz działa. Jeśli ktoś nadal napotyka problem po wykonaniu podanych tutaj odpowiedzi, upewnij się, że wersje w pliku pom.xml są również poprawne. Zmień wersje, aby zobaczyć, czy to działa. Jeśli tak, napraw problem z wersją.
Wierzę, że moja odpowiedź będzie specyficzna dla Intellij.
Odbudowałem czysty, nawet posuwam się do ręcznego usunięcia katalogów „out” i „target”. Intellij ma funkcję „unieważniania pamięci podręcznej i restartowania”, która czasami usuwa dziwne błędy. Tym razem to nie zadziałało. Wszystkie wersje zależności wyglądały poprawnie w menu Ustawienia projektu-> moduły.
Ostateczną odpowiedzią było ręczne usunięcie mojej zależności problemu od mojego lokalnego repozytorium maven. Winowajcą była stara wersja bouncycastle (wiedziałem, że właśnie zmieniłem wersje i to byłby problem) i chociaż stara wersja nie pokazywała, gdzie jest budowana, rozwiązała mój problem. Podczas tego procesu korzystałem z wersji Intellij 14, a następnie zaktualizowałem ją do 15.
W moim przypadku napotkałem ten błąd w ten sposób. pom.xml
mojego projektu zdefiniował dwie zależności A
i B
. I oba A
i B
określono zależność od samego artefaktu (nazywają to C
), ale w różnych wersjach nim ( C.1
i C.2
). Kiedy tak się dzieje, dla każdej klasy w C
maven można wybrać tylko jedną wersję klasy z dwóch wersji (podczas budowania słoika ). Wybierze „najbliższą” wersję na podstawie swoich reguł mediacji zależności i wyświetli ostrzeżenie „Mamy zduplikowaną klasę ...” Jeśli podpis metody / klasy zmienia się między wersjami, może to spowodować java.lang.IncompatibleClassChangeError
wyjątek, jeśli niepoprawna wersja jest używane w czasie wykonywania.
Zaawansowane: Jeśli A
muszą używać V1 C
i B
musi korzystać z v2 C
, to musimy przeprowadzić C
w A
i B
„s poms do konfliktu klasowego unikać (mamy duplikat klasy ostrzeżenie) przy tworzeniu ostatecznego projektu, który zależy zarówno A
i B
.
W moim przypadku błąd pojawił się, gdy dodałem com.nimbusds
bibliotekę do mojej aplikacji wdrożonej na Websphere 8.5
.
Wystąpił następujący wyjątek:
Przyczyna: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
Rozwiązaniem było wykluczenie słoika asm z biblioteki:
<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>
Jeśli jest to zapis możliwych wystąpień tego błędu, wówczas:
Właśnie dostałem ten błąd na WAS (8.5.0.1), podczas ładowania CXF (2.6.0) konfiguracji sprężyny (3.1.1_release), w której wyjątek BeanInstantiationException zwrócił wyjątek CXF ExtensionException, zwinięcie IncompatibleClassChangeError. Poniższy fragment pokazuje istotę śladu stosu:
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
W tym przypadku rozwiązaniem była zmiana kolejności ścieżek klas modułu w moim pliku wojny. Oznacza to, że otwórz aplikację wojenną w konsoli WAS pod i wybierz moduły klienta. W konfiguracji modułu ustaw ładowanie klasy na „nadrzędny jako ostatni”.
Można to znaleźć w konsoli WAS:
Dokumentowanie innego scenariusza po spaleniu zdecydowanie za dużo czasu.
Upewnij się, że nie masz jar zależności, który ma klasę z adnotacją EJB.
Mieliśmy wspólny plik jar zawierający @local
adnotację. Ta klasa została później przeniesiona z tego wspólnego projektu do naszego głównego projektu słoika ejb. Nasz słoik ejb i wspólny słoik są umieszczone w jednym uchu. Wersja naszej wspólnej zależności jar nie została zaktualizowana. Zatem 2 klasy próbują być czymś z niezgodnymi zmianami.
Z jakiegoś powodu ten sam wyjątek jest zgłaszany również przy użyciu JNI i przekazaniu argumentu jclass zamiast zadania zadania podczas wywoływania a Call*Method()
.
Jest to podobne do odpowiedzi z 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;
}
Wiem, że trochę za późno jest odpowiedzieć na to pytanie 5 lat po zadaniu pytania, ale jest to jeden z największych hitów podczas wyszukiwania, java.lang.IncompatibleClassChangeError
więc chciałem udokumentować ten szczególny przypadek.
Dodając moje 2 centy. Jeśli używasz scala i sbt i logowania jako zależności, może się to zdarzyć, ponieważ wcześniejsza wersja scala-logingu miała nazwę scala-logging-api. Tak więc, zasadniczo, rozwiązania zależności nie występują z powodu różnych nazwy prowadzące do błędów w czasie wykonywania podczas uruchamiania aplikacji Scala.
Dodatkową przyczyną tego problemu jest Instant Run
włączenie Android Studio.
Jeśli zauważysz, że zaczynasz otrzymywać ten błąd, wyłącz go Instant Run
.
Instant Run
modyfikuje wiele rzeczy podczas programowania, aby przyspieszyć dostarczanie aktualizacji do uruchomionej aplikacji. Stąd natychmiastowy bieg. Kiedy to działa, jest naprawdę przydatne. Jednak gdy pojawia się taki problem, najlepiej jest go wyłączyć Instant Run
do czasu wydania kolejnej wersji Androida Studio.