我正在将一个Java库打包为JAR文件,在尝试调用其中的方法时,会抛出很多java.lang.IncompatibleClassChangeError
错误,这些错误似乎是随机出现的。可能会出现哪些问题导致这个错误?
我正在将一个Java库打包为JAR文件,在尝试调用其中的方法时,会抛出很多java.lang.IncompatibleClassChangeError
错误,这些错误似乎是随机出现的。可能会出现哪些问题导致这个错误?
static
非私有字段/方法更改为static
或反之亦然。*.class
文件)并重新编译吗?编辑文件也会产生类似的效果。 - notnoop你的新打包库与旧版本不具备向后二进制兼容性(BC)。因此,一些未重新编译的库客户端可能会抛出异常。
这是 Java 库 API 中引起使用旧版本库构建的客户端在新版本上运行时可能抛出 java.lang.IncompatibleClassChangeError 的更改的完整列表(即破坏了 BC):
注意:其他不兼容的更改也会导致许多其他异常:NoSuchFieldError、NoSuchMethodError、IllegalAccessError、InstantiationError、VerifyError、NoClassDefFoundError 和 AbstractMethodError。
关于 BC 的最好论文是 Jim des Rivières 写的 “Evolving Java-based APIs 2: Achieving API Binary Compatibility”。
还有一些自动工具可以检测这种更改:
使用 japi-compliance-checker 检查你的库:
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
或其他类似的异常)。
如果行为看起来随机,那么很可能是多线程程序在类加载不同传递依赖项时基于哪些代码被首先执行而导致的。
要解决这些问题,请尝试以-verbose
作为参数启动VM,然后查看在异常发生时正在加载的类。你应该会看到一些令人惊讶的信息。例如,同时具有多个副本的相同依赖项和版本,如果你知道它们被包含了,你可能从未接受过或预期过。
使用Maven解决重复的JAR包最好结合使用Maven的maven-dependency-plugin 和 maven-enforcer-plugin,或者使用SBT的Dependency Graph Plugin。然后将这些JAR包添加到您的顶级POM文件的< dependencyManagement >部分或作为SBT中导入的依赖项元素(以删除这些依赖项)。
祝你好运!
示例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编译的。
实际上,原因是存在重复库,其中一个位于类路径中,另一个包含在位于类路径内的jar文件中。
pom.xml
定义了两个依赖项 A
和 B
。 而且,A
和 B
都定义了对同一构件(称其为 C
)的依赖,但是它们使用了不同的版本 (C.1
和 C.2
)。 当出现这种情况时,对于 C
中的每个类,Maven 只能从两个版本中选择一个版本的类 (在构建 uber-jar 时)。 它将根据其 依赖关系管理 规则选择“最近”的版本,并输出警告 "We have a duplicate class..."。 如果方法/类签名在版本之间发生更改,则如果在运行时使用了不正确的版本,则可能会导致 java.lang.IncompatibleClassChangeError
异常。
高级:如果 A
必须使用 C
的 v1 版本,而 B
必须使用 C
的 v2 版本,则我们必须将 C
在 A
和 B
的 POM 文件中重新定位,以避免在构建最终依赖于 A
和 B
的项目时出现重复类警告。
<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>
我在使用glassfish卸载和重新部署war文件时遇到了这个问题。我的类结构像这样,
public interface A{
}
public class AImpl implements A{
}
它被更改为
public abstract class A{
}
public class AImpl extends A{
}
停止并重新启动域后,它正常工作了。我使用的是Glassfish 3.1.43。
出于某种原因,我正在进行一次大的重构,并开始遇到这个问题。我重命名了接口所在的包,问题得到解决。希望这可以帮到你。