Sto impacchettando una libreria Java come JAR, e mi lancia molti java.lang.IncompatibleClassChangeError
s quando provo a invocare metodi da essa. Questi errori sembrano apparire a caso. Quali tipi di problemi potrebbero causare questo errore?
Sto impacchettando una libreria Java come JAR, e mi lancia molti java.lang.IncompatibleClassChangeError
s quando provo a invocare metodi da essa. Questi errori sembrano apparire a caso. Quali tipi di problemi potrebbero causare questo errore?
Risposte:
Ciò significa che hai apportato alcune modifiche binarie incompatibili alla libreria senza ricompilare il codice client. Java Language Specification §13 descrive in dettaglio tutte queste modifiche, soprattutto, cambiando static
campi / metodi non non privati in modo che siano static
o viceversa.
Ricompila il codice client con la nuova libreria e dovresti essere pronto.
AGGIORNAMENTO: se si pubblica una biblioteca pubblica, è necessario evitare il più possibile di apportare modifiche binarie incompatibili per preservare la cosiddetta "compatibilità binaria con le versioni precedenti". L'aggiornamento dei soli barattoli delle dipendenze idealmente non dovrebbe interrompere l'applicazione o la build. Se è necessario interrompere la compatibilità binaria con le versioni precedenti, si consiglia di aumentare il numero di versione principale (ad es. Da 1.xy a 2.0.0) prima di rilasciare la modifica.
*.class
file) e ricompilare? La modifica dei file ha un effetto simile.
La libreria appena impacchettata non è compatibile con le versioni precedenti binarie (BC) con la versione precedente. Per questo motivo alcuni client della libreria non ricompilati potrebbero generare l'eccezione.
Questo è un elenco completo delle modifiche nell'API della libreria Java che può causare il lancio di java.lang ai client creati con una versione precedente della libreria. IncompatibleClassChangeError se vengono eseguiti su uno nuovo (ovvero interrompendo BC):
Nota : Ci sono molte altre eccezioni causate da altre modifiche incompatibili: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError e AbstractMethodError .
L'articolo migliore su BC è "Evolving API based Java: Achieving API Binibility Compatibility" scritto da Jim des Rivières.
Esistono anche alcuni strumenti automatici per rilevare tali cambiamenti:
Utilizzo di japi-compliance-checker per la tua libreria:
japi-compliance-checker OLD.jar NEW.jar
Utilizzo dello strumento clirr:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
In bocca al lupo!
Mentre queste risposte sono tutte corrette, spesso risolvere il problema è più difficile. Generalmente è il risultato di due versioni leggermente diverse della stessa dipendenza dal percorso di classe ed è quasi sempre causata da una superclasse diversa da quella originariamente compilata per essere diversa dal percorso di classe o dall'importanza di una diversa chiusura transitiva, ma generalmente a classe istanza e invocazione del costruttore. (Dopo il caricamento della classe e l'invocazione del ctor riusciti, otterrai NoSuchMethodException
o quant'altro.)
Se il comportamento appare casuale, è probabilmente il risultato di un programma multithread che carica diverse dipendenze transitive in base al codice che è stato colpito per primo.
Per risolverli, prova ad avviare la VM con -verbose
come argomento, quindi guarda le classi che venivano caricate quando si verifica l'eccezione. Dovresti vedere alcune informazioni sorprendenti. Ad esempio, avere più copie della stessa dipendenza e versioni che non ti saresti mai aspettato o che avresti accettato se sapessi che sarebbero state incluse.
La risoluzione di vasetti duplicati con Maven viene eseguita al meglio con una combinazione del plug- in Maven -Dipendenza e del plugin Maven -Enforcer in Maven (o Plugin del grafico delle dipendenze SBT , quindi aggiungendo tali vasetti a una sezione del POM di livello superiore o come dipendenza importata elementi in SBT (per rimuovere tali dipendenze).
In bocca al lupo!
Ho anche scoperto che, quando si utilizza JNI, invocando un metodo Java dal C ++, se si passano i parametri al metodo Java richiamato nell'ordine sbagliato, si otterrà questo errore quando si tenta di utilizzare i parametri all'interno del metodo chiamato (perché non sarà il tipo giusto). Inizialmente sono rimasto sorpreso dal fatto che JNI non effettui questo controllo per te come parte del controllo della firma di classe quando invochi il metodo, ma presumo che non eseguano questo tipo di controllo perché potresti passare parametri polimorfici e devono supponiamo che tu sappia cosa stai facendo.
Esempio di codice 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.
}
Esempio di codice 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...
}
}
}
Ho avuto lo stesso problema e in seguito ho scoperto che sto eseguendo l'applicazione su Java versione 1.4 mentre l'applicazione è compilata sulla versione 6.
In realtà, il motivo era di avere una libreria duplicata, una si trova all'interno del classpath e l'altra è inclusa in un file jar che si trova all'interno del classpath.
Un'altra situazione in cui questo errore può apparire è con Emma Code Coverage.
Ciò accade quando si assegna un oggetto a un'interfaccia. Immagino che questo abbia qualcosa a che fare con l'oggetto che viene strumentato e non più compatibile con i binari.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
Fortunatamente questo problema non si verifica con Cobertura, quindi ho aggiunto cobertura-maven-plugin nei miei plugin di segnalazione del mio pom.xml
Ho affrontato questo problema mentre non dispiegavo e ridistribuivo una guerra con il pesce di vetro. La mia struttura di classe era così,
public interface A{
}
public class AImpl implements A{
}
ed è stato modificato in
public abstract class A{
}
public class AImpl extends A{
}
Dopo aver arrestato e riavviato il dominio, ha funzionato bene. Stavo usando glassfish 3.1.43
Ho un'applicazione web che si implementa perfettamente sul mio tomcat della mia macchina locale (8.0.20). Tuttavia, quando l'ho inserito nell'ambiente qa (tomcat - 8.0.20), continuava a darmi IncompatibleClassChangeError e mi lamentavo che mi stavo estendendo su un'interfaccia. Questa interfaccia è stata cambiata in una classe astratta. E ho compilato le classi genitore e figlio e continuavo a ottenere lo stesso problema. Infine, volevo eseguire il debug, quindi ho modificato la versione del genitore in x.0.1-SNAPSHOT e quindi ho compilato tutto e ora funziona. Se qualcuno continua a risolvere il problema dopo aver seguito le risposte fornite qui, assicurati che anche le versioni in pom.xml siano corrette. Cambia le versioni per vedere se funziona. In tal caso, quindi risolvere il problema di versione.
La mia risposta, credo, sarà specifica di Intellij.
Avevo ricostruito pulito, arrivando persino a eliminare manualmente le directory "out" e "target". Intellij ha un "invalidare le cache e riavviare", che a volte cancella gli errori dispari. Questa volta non ha funzionato. Le versioni delle dipendenze sembravano tutte corrette nel menu Impostazioni-> Moduli del progetto.
La risposta finale è stata quella di eliminare manualmente la dipendenza dal mio problema dal repository Maven locale. Il colpevole era una vecchia versione di bouncycastle (sapevo di aver appena cambiato versione e che sarebbe stato questo il problema) e sebbene la vecchia versione non comparisse da nessuna parte in ciò che veniva costruito, risolveva il mio problema. Stavo usando intellij versione 14 e quindi aggiornato a 15 durante questo processo.
Nel mio caso, ho riscontrato questo errore in questo modo. pom.xml
del mio progetto ha definito due dipendenze A
e B
. Ed entrambi A
e B
la dipendenza definita sullo stesso manufatto (lo chiamano C
), ma diverse versioni di esso ( C.1
e C.2
). Quando ciò accade, per ogni classe in C
Maven è possibile selezionare solo una versione della classe tra le due versioni (durante la creazione di un Uber-Jar ). Selezionerà la versione "più vicina" in base alle sue regole di mediazione di dipendenza e genererà un avviso "Abbiamo una classe duplicata ..." Se una firma metodo / classe cambia tra le versioni, può causare java.lang.IncompatibleClassChangeError
un'eccezione se la versione errata è utilizzato in fase di esecuzione.
Avanzato: se è A
necessario utilizzare v1 di C
e B
deve utilizzare v2 di C
, è necessario riposizionare C
in A
e B
'poms per evitare conflitti di classe (abbiamo un avviso di classe duplicato) quando si costruisce il progetto finale che dipende da entrambi A
e B
.
Nel mio caso l'errore è apparso quando ho aggiunto la com.nimbusds
libreria nella mia applicazione distribuita su Websphere 8.5
.
Si è verificata la seguente eccezione:
Causato da: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
La soluzione era di escludere il barattolo asm dalla libreria:
<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>
Verifica se il tuo codice non è composto da due progetti di moduli con gli stessi nomi di classi e definizione di pacchetti. Ad esempio, ciò potrebbe accadere se qualcuno utilizza copia-incolla per creare una nuova implementazione dell'interfaccia in base all'implementazione precedente.
Se questo è un record di possibili occorrenze di questo errore, allora:
Ho appena ricevuto questo errore su WAS (8.5.0.1), durante il caricamento CXF (2.6.0) della configurazione della molla (3.1.1_release) in cui una BeanInstantiationException ha eseguito il rollup di una CXF ExtensionException, eseguendo il roll-up di IncompatibleClassChangeError. Il frammento seguente mostra l'essenza della traccia dello stack:
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
In questo caso, la soluzione era cambiare l'ordine dei percorsi di classe del modulo nel mio file di guerra. Cioè, apri l'applicazione di guerra nella console WAS sotto e seleziona i moduli client. Nella configurazione del modulo, impostare il caricamento della classe su "padre ultimo".
Questo si trova nella console WAS:
Documentare un altro scenario dopo aver bruciato troppo tempo.
Assicurati di non avere un vaso delle dipendenze con una classe con un'annotazione EJB su di essa.
Avevamo un file jar comune con @local
un'annotazione. Quella classe è stata successivamente spostata da quel progetto comune e nel nostro principale progetto ejb jar. Il nostro vaso ejb e il nostro vaso comune sono entrambi raggruppati in un orecchio. La versione della nostra dipendenza jar comune non è stata aggiornata. Quindi 2 classi cercano di essere qualcosa con cambiamenti incompatibili.
Per qualche ragione la stessa eccezione viene generata anche quando si utilizza JNI e si passa l'argomento jclass invece del jobject quando si chiama a Call*Method()
.
Questo è simile alla risposta di Ogre Salmo33.
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;
}
So che è un po 'tardi per rispondere a questa domanda 5 anni dopo essere stato chiesto, ma questo è uno dei migliori successi durante la ricerca, java.lang.IncompatibleClassChangeError
quindi ho voluto documentare questo caso speciale.
Aggiungendo i miei 2 centesimi. Se stai usando scala e sbt e scala-logging come dipendenza; allora questo può accadere perché la versione precedente di scala-logging aveva il nome scala-logging-api.Quindi, essenzialmente le risoluzioni di dipendenza non si verificano a causa di diversi nomi che portano a errori di runtime durante l'avvio dell'applicazione scala.
Un'ulteriore causa di questo problema è se hai Instant Run
abilitato Android Studio.
Se scopri di iniziare a ricevere questo errore, disattiva Instant Run
.
Instant Run
modifica un gran numero di cose durante lo sviluppo, per rendere più veloce la fornitura di aggiornamenti per l'app in esecuzione. Quindi corsa istantanea. Quando funziona, è davvero utile. Tuttavia, quando si verifica un problema come questo, la cosa migliore da fare è disattivare Instant Run
fino alla prossima versione di Android Studio.