Bir Java kütüphanesini JAR olarak paketliyorum ve ondan java.lang.IncompatibleClassChangeError
yöntemleri çağırmaya çalıştığımda birçok s atıyor . Bu hatalar rastgele görünüyor. Bu hataya ne tür sorunlar neden olabilir?
Bir Java kütüphanesini JAR olarak paketliyorum ve ondan java.lang.IncompatibleClassChangeError
yöntemleri çağırmaya çalıştığımda birçok s atıyor . Bu hatalar rastgele görünüyor. Bu hataya ne tür sorunlar neden olabilir?
Yanıtlar:
Bu, istemci kodunu yeniden derlemeden kitaplıkta bazı uyumsuz ikili değişiklikler yaptığınız anlamına gelir. Java Dil Belirtimi §13 , bu tür tüm değişiklikleri, en belirgin şekilde, static
özel olmayan alanları / yöntemleri değiştirmeyi static
veya tersini değiştirmeyi ayrıntılarıyla açıklar .
İstemci kodunu yeni kitaplığa karşı yeniden derlediğinizde işe koyulmanız iyi olur.
GÜNCELLEME: Bir halk kütüphanesi yayınlıyorsanız, "ikili geriye dönük uyumluluk" olarak bilinenleri korumak için mümkün olduğunca uyumsuz ikili değişiklikler yapmaktan kaçınmalısınız. Tek başına bağımlılık kavanozlarını güncellemek, uygulamayı veya yapıyı bozmamalıdır. İkili geriye dönük uyumluluğu kırmanız gerekiyorsa, değişikliği yayınlamadan önce ana sürüm numarasının (ör. 1.xy'den 2.0.0'a) artırılması önerilir .
*.class
dosyaları sil ) ve yeniden derleme yapmaya çalıştınız mı ? Dosya düzenleme benzer etkiye sahiptir.
Yeni paketlenmiş kitaplığınız eski sürümle geriye dönük ikili uyumlu (BC) değildir . Bu nedenle, yeniden derlenmeyen bazı kitaplık istemcileri özel durum atabilir.
Bu, kitaplığın eski bir sürümüyle oluşturulan istemcilerin java.lang atmasına neden olabilecek Java kitaplığı API'sindeki değişikliklerin tam bir listesidir. IncompatibleClassChangeError yeni bir tane üzerinde çalışırlarsa (yani BC'yi kırmak):
Not : Birçok vardır diğer istisnalar : diğer uyumsuz değişikliklerin neden NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError ve AbstractMethodError .
BC hakkında daha iyi bir makale Jim des Rivières tarafından yazılan "Java tabanlı API'leri Geliştirme 2: API İkili Uyumluluğunu Sağlama" dır .
Bu değişiklikleri tespit etmek için bazı otomatik araçlar da vardır :
Kütüphaneniz için japi-uygunluk denetleyicisinin kullanımı:
japi-compliance-checker OLD.jar NEW.jar
Clirr aracının kullanımı:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
İyi şanslar!
Bu yanıtların hepsi doğru olmakla birlikte, sorunun çözümü genellikle daha zordur. Genellikle, sınıfyoluna aynı bağımlılığın iki farklı hafif versiyonunun sonucudur ve neredeyse her zaman ya başlangıçta sınıf yolunda olmaktan ya da geçişli kapamanın bazı ithalatı farklı olmaktan farklı olarak, ancak genellikle sınıfta olmaktan farklı bir üst sınıftan kaynaklanır. örnekleme ve kurucu çağırma. (Başarılı sınıf yükleme ve ctor çağırma işleminden sonra, alırsınız NoSuchMethodException
ya da almazsınız .)
Davranış rasgele görünüyorsa, büyük olasılıkla ilk önce hangi kodun vurulduğuna bağlı olarak farklı geçiş bağımlılıkları yükleyen çok iş parçacıklı bir program sınıfının sonucudur.
Bunları çözmek için VM'yi -verbose
bağımsız değişken olarak başlatmayı deneyin , ardından kural dışı durum oluştuğunda yüklenen sınıflara bakın. Bazı şaşırtıcı bilgiler görmelisiniz. Örneğin, aynı bağımlılığa sahip birden fazla kopyaya ve bunların dahil edildiğini bildiğinizde hiç beklemediğiniz veya kabul etmediğiniz sürümlere sahip olması.
Maven ile yinelenen kavanozların çözülmesi en iyi şekilde Maven (veya SBT'nin Bağımlılık Grafiği Eklentisi) altındaki maven bağımlılığı eklentisi ve maven-uygulayıcı eklentisinin bir kombinasyonu ile yapılır , daha sonra bu kavanozları üst düzey POM'nizin bir bölümüne veya içe aktarılan bağımlılık olarak ekleyin SBT'deki öğeler (bu bağımlılıkları kaldırmak için).
İyi şanslar!
Ayrıca, JNI kullanırken, C ++ 'dan bir Java yöntemi çağrıldığında, yanlış sırada çağrılan Java yöntemine parametreleri iletirseniz, (çünkü onlar çağrılan yöntem içindeki parametreleri kullanmaya çalıştığınızda bu hata alırsınız) keşfettim doğru tür olmayacak). Başlangıçta JNI yöntemini çağırdığınızda sınıf imzası kontrolünün bir parçası olarak sizin için bu kontrolü yapmaz, ama polimorfik parametreleri geçiriyor olabilirsiniz ve onlar yapmak zorunda olduğunu varsayıyorum ne yaptığınızı bildiğinizi varsayalım.
Örnek C ++ JNI Kodu:
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.
}
Örnek Java Kodu:
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...
}
}
}
Aynı sorunu yaşadım ve daha sonra uygulama sürüm 6'da derlenirken uygulamayı Java sürüm 1.4 üzerinde çalıştırdığımı anladım.
Aslında, yinelenen bir kitaplığa sahip olmanın nedeni, biri sınıfyolunun içinde, diğeri sınıf yolunda bulunan bir kavanoz dosyasının içine dahil edilmişti.
Bu hatanın ortaya çıkabileceği başka bir durum da Emma Kod Kapsamı'dır.
Bu, bir arabirime bir Nesne atandığında olur. Sanırım bu nesnenin artık ikili uyumlu değil araçlı olmasıyla bir ilgisi var.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
Neyse ki Cobertura ile bu sorun olmaz, bu yüzden pom.xml raporlama eklentilerime cobertura-maven-plugin ekledim
Glassfish ile savaşı çözerken ve yeniden dağıtırken bu sorunla karşılaştım. Sınıf yapım şöyleydi,
public interface A{
}
public class AImpl implements A{
}
ve olarak değiştirildi
public abstract class A{
}
public class AImpl extends A{
}
Etki alanını durdurup yeniden başlattıktan sonra iyi çalıştı. Glassfish 3.1.43 kullanıyordum
Yerel makinemin tomcat'ında (8.0.20) mükemmel şekilde dağıtılan bir web uygulamam var. Ancak, qa ortamına koyduğumda (tomcat - 8.0.20), bana IncompatibleClassChangeError vermeyi sürdürdü ve bir arabirime genişlettiğimden şikayet ediyordu. Bu arayüz soyut bir sınıfa dönüştürüldü. Ve ebeveyn ve çocuk sınıflarını derledim ve hala aynı sorunu almaya devam ettim. Son olarak, hata ayıklamak istedim, bu yüzden üst sürümdeki sürümü x.0.1-SNAPSHOT olarak değiştirdim ve sonra her şeyi derledim ve şimdi çalışıyor. Burada verilen yanıtları izledikten sonra birisi hala sorunu çözüyorsa, lütfen pom.xml dosyasındaki sürümlerin de doğru olduğundan emin olun. Bunun işe yarayıp yaramadığını görmek için sürümleri değiştirin. Öyleyse, sürüm sorununu düzeltin.
Cevabımın Intellij'e özgü olacağına inanıyorum.
Ben bile "out" ve "target" dirs silmek gibi gidiyor temiz, yeniden inşa vardı. Intellij, bazen garip hataları silen bir "önbellekleri geçersiz kıl ve yeniden başlat" özelliğine sahiptir. Bu sefer işe yaramadı. Bağımlılık sürümlerinin tümü proje ayarları-> modüller menüsünde doğru görünüyordu.
Son cevap, yerel bağımlılık depomdan sorun bağımlılığımı el ile silmekti. Bouncycastle'ın eski bir versiyonu suçluydu (sadece sürümleri değiştirdiğimi biliyordum ve bu sorun olurdu) ve eski sürüm inşa edilen yerde hiçbir şey göstermese de sorunumu çözdü. Intellij 14 sürümünü kullanıyordum ve bu işlem sırasında 15'e yükselttim.
Benim durumumda, bu hatayla bu şekilde karşılaştım. pom.xml
Projemin iki bağımlılıkları tanımlanır A
ve B
. Ve her ikisi de A
ve B
aynı eser (diyoruz üzerinde tanımlı bağımlılık C
) ama (farklı sürümleri C.1
ve C.2
). Bu olduğunda, C
maven'deki her sınıf için iki sürümden sınıfın yalnızca bir sürümünü seçebilir ( uber-jar oluştururken ). Bağımlılık uyumlulaştırma kurallarına göre "en yakın" sürümü seçer ve " Yinelenen bir sınıfımız var ..." uyarısı verir. Bir yöntem / sınıf imzası sürümler arasında değişirse java.lang.IncompatibleClassChangeError
, yanlış sürüm çalışma zamanında kullanılır.
Gelişmiş: Eğer A
bir v1 kullanmalıdır C
ve B
bir v2 kullanmalıdır C
o zaman gerekir, taşınmaya C
içinde A
ve B
hem bağlıdır nihai projeyi oluştururken önlemek sınıf çatışması 'ın ponlar (biz yinelenen bir sınıf uyarı var) A
ve B
.
Benim durumumda, com.nimbusds
dağıtılan uygulamamdaki kitaplığı eklediğimde hata ortaya çıktı Websphere 8.5
.
Aşağıdaki istisna oluştu:
Nedeni: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
Çözüm asm kavanozunu kütüphaneden hariç tutmaktı:
<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>
Bu, bu hatanın olası oluşumlarının bir kaydıysa:
Ben sadece bir BeanInstantiationException CXF ExtensionException bir IncompatibleClassChangeError yuvarlandı bahar (3.1.1_release) yapılandırma CXF (2.6.0) yükleme sırasında WAS (8.5.0.1), bu hatayı aldım. Aşağıdaki snippet, yığın izlemesinin özünü gösterir:
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
Bu durumda çözüm, savaş dosyamdaki modülün sınıf yolu sırasını değiştirmekti. Yani, aşağıdaki WAS konsolunda savaş uygulamasını açın ve istemci modüllerini seçin. Modül yapılandırmasında, sınıf yüklemesini "son ana öğe" olarak ayarlayın.
WAS konsolunda bulunur:
Çok fazla zaman harcadıktan sonra başka bir senaryoyu belgelemek.
Üzerinde EJB ek açıklaması olan bir sınıfa sahip bir bağımlılık kavanozunuz olmadığından emin olun.
@local
Ek açıklama içeren ortak bir kavanoz dosyamız vardı . Bu sınıf daha sonra bu ortak projeden ve ana ejb kavanoz projemize taşındı. Bizim ejb kavanozumuz ve ortak kavanozumuz bir kulak içinde paketlenmiştir. Ortak kavanoz bağımlılığımızın sürümü güncellenmedi. Böylece 2 sınıf uyumsuz değişikliklerle bir şey olmaya çalışıyor.
Bazı nedenlerden dolayı, JNI kullanılırken ve a çağrılırken jobject yerine jclass argümanını iletirken aynı istisna atılır Call*Method()
.
Bu Ogre Psalm33'ün cevabına benzer.
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;
}
Sorulduktan 5 yıl sonra bu soruya cevap vermek için biraz geç olduğunu biliyorum, ancak bu arama yaparken en iyi hitlerden biri bu java.lang.IncompatibleClassChangeError
yüzden bu özel vakayı belgelemek istedim.
Bu sorunun ek bir nedeni, Instant Run
Android Studio için etkinleştirmiş olmanızdır .
Bu hatayı almaya başladığınızı fark ederseniz kapatın Instant Run
.
Instant Run
çalışan Uygulamanıza güncelleme sağlamak için daha hızlı hale getirmek amacıyla geliştirme sırasında çok sayıda şeyi değiştirir. Böylece anında çalışma. Çalıştığında, gerçekten yararlıdır. Ancak, bunun gibi bir sorun ortaya çıktığında, yapılacak en iyi şey Instant Run
Android Studio'nun bir sonraki sürümüne kadar kapatmaktır .