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?
218
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 nastatic
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.
źródło
*.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:
Zastosowanie narzędzia Clirr:
Powodzenia!
źródło
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!
źródło
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 ++:
Przykładowy kod Java:
źródło
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.
źródło
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
źródło
Napotkałem ten problem podczas cofania i ponownego wdrażania wojny ze szklistymi rybami. Moja struktura klasowa była taka,
i został zmieniony na
Po zatrzymaniu i ponownym uruchomieniu domeny wszystko poszło dobrze. Używałem szklanych ryb 3.1.43
źródło
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ą.
źródło
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.
źródło
W moim przypadku napotkałem ten błąd w ten sposób.
pom.xml
mojego projektu zdefiniował dwie zależnościA
iB
. I obaA
iB
określono zależność od samego artefaktu (nazywają toC
), ale w różnych wersjach nim (C.1
iC.2
). Kiedy tak się dzieje, dla każdej klasy wC
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ć V1C
iB
musi korzystać z v2C
, to musimy przeprowadzićC
wA
iB
„s poms do konfliktu klasowego unikać (mamy duplikat klasy ostrzeżenie) przy tworzeniu ostatecznego projektu, który zależy zarównoA
iB
.źródło
W moim przypadku błąd pojawił się, gdy dodałem
com.nimbusds
bibliotekę do mojej aplikacji wdrożonej naWebsphere 8.5
.Wystąpił następujący wyjątek:
Rozwiązaniem było wykluczenie słoika asm z biblioteki:
źródło
Sprawdź, czy twój kod nie składa się z dwóch projektów modułów, które mają takie same nazwy klas i definicje pakietów. Może się to na przykład zdarzyć, jeśli ktoś użyje funkcji kopiuj-wklej, aby utworzyć nową implementację interfejsu w oparciu o poprzednią implementację.
źródło
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:
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:
źródło
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.źródło
Wszystkie powyższe - z jakiegokolwiek powodu robiłem jakiś duży refaktor i zacząłem to otrzymywać. Zmieniłem nazwę pakietu, w którym znajdował się mój interfejs, i to go wyczyściło. Mam nadzieję, że to pomaga.
źródło
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.
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.źródło
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.
źródło
Dodatkową przyczyną tego problemu jest
Instant Run
włączenie Android Studio.Poprawka
Jeśli zauważysz, że zaczynasz otrzymywać ten błąd, wyłącz go
Instant Run
.Czemu
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.źródło