什么原因导致java.lang.CompatibleClassChangeError?

什么原因导致java.lang.CompatibleClassChangeError?,java,jar,runtime-error,binary-compatibility,Java,Jar,Runtime Error,Binary Compatibility,我将Java库打包为一个JAR,当我试图从它调用方法时,它抛出了许多Java.lang.CompatibleClassChangeErrors。这些错误似乎是随机出现的。什么样的问题可能会导致此错误?这意味着您在没有重新编译客户端代码的情况下对库进行了一些不兼容的二进制更改。详细说明所有此类更改,最突出的是将非静态非私有字段/方法更改为静态,反之亦然 根据新库重新编译客户机代码,您应该可以开始了 更新:如果发布公共库,应尽可能避免进行不兼容的二进制更改,以保持所谓的“二进制向后兼容性”。理想情况

我将Java库打包为一个JAR,当我试图从它调用方法时,它抛出了许多
Java.lang.CompatibleClassChangeError
s。这些错误似乎是随机出现的。什么样的问题可能会导致此错误?

这意味着您在没有重新编译客户端代码的情况下对库进行了一些不兼容的二进制更改。详细说明所有此类更改,最突出的是将非
静态
非私有字段/方法更改为
静态
,反之亦然

根据新库重新编译客户机代码,您应该可以开始了


更新:如果发布公共库,应尽可能避免进行不兼容的二进制更改,以保持所谓的“二进制向后兼容性”。理想情况下,单独更新依赖项jar不会破坏应用程序或构建。如果您必须中断二进制向后兼容性,则在发布更改之前必须增加主要版本号(例如,从1.x.y增加到2.0.0)。

这意味着您在不重新编译客户端代码的情况下对库进行了一些不兼容的二进制更改。详细说明所有此类更改,最突出的是将非
静态
非私有字段/方法更改为
静态
,反之亦然

根据新库重新编译客户机代码,您应该可以开始了


更新:如果发布公共库,应尽可能避免进行不兼容的二进制更改,以保持所谓的“二进制向后兼容性”。理想情况下,单独更新依赖项jar不会破坏应用程序或构建。如果您必须中断二进制向后兼容,则在发布更改之前必须增加主要版本号(例如从1.x.y增加到2.0.0)。

您新打包的库与旧版本不向后二进制兼容。因此,一些未重新编译的库客户端可能会引发异常

这是Java库API中更改的完整列表,这些更改可能会导致使用旧版本库构建的客户端抛出Java.lang.不兼容ClassChangeError,如果它们在新版本上运行(即破坏BC):

  • 非最终字段变为静态
  • 非恒定场变为非静态场
  • 类成为接口
  • 接口成为类
  • 如果将新字段添加到类/接口(或添加新的超级类/超级接口),则客户端类C的超级接口中的静态字段可能隐藏从C的超级类继承的添加字段(具有相同名称)(非常罕见的情况)
  • 注意:其他不兼容的更改导致了许多异常:NoSuchFieldError、NoSuchMethodError、IllegaAccessError、InstanceionError、VerifyError、NoClassDefFoundError和AbstractMethodError

    关于不列颠哥伦比亚省的更好的论文是吉姆·德·里维埃尔写的

    还有一些自动工具用于检测此类更改:

    为您的库使用japi合规性检查器:

    japi-compliance-checker OLD.jar NEW.jar
    
    clirr工具的使用:

    java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
    

    祝你好运

    新打包的库与旧版本不向后二进制兼容。因此,一些未重新编译的库客户端可能会引发异常

    这是Java库API中更改的完整列表,这些更改可能会导致使用旧版本库构建的客户端抛出Java.lang.不兼容ClassChangeError,如果它们在新版本上运行(即破坏BC):

  • 非最终字段变为静态
  • 非恒定场变为非静态场
  • 类成为接口
  • 接口成为类
  • 如果将新字段添加到类/接口(或添加新的超级类/超级接口),则客户端类C的超级接口中的静态字段可能隐藏从C的超级类继承的添加字段(具有相同名称)(非常罕见的情况)
  • 注意:其他不兼容的更改导致了许多异常:NoSuchFieldError、NoSuchMethodError、IllegaAccessError、InstanceionError、VerifyError、NoClassDefFoundError和AbstractMethodError

    关于不列颠哥伦比亚省的更好的论文是吉姆·德·里维埃尔写的

    还有一些自动工具用于检测此类更改:

    为您的库使用japi合规性检查器:

    japi-compliance-checker OLD.jar NEW.jar
    
    clirr工具的使用:

    java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
    

    祝你好运

    >我也发现,当使用JNI时,从C++调用java方法,如果将参数以错误的顺序传递给调用java方法,则当尝试使用调用方法中的参数时,会得到此错误(因为它们不是正确的类型)。我最初感到惊讶的是,当您调用该方法时,JNI没有为您执行这种检查,作为类签名检查的一部分,但我假设他们没有执行这种检查,因为您可能正在传递多态参数,他们必须假设您知道您在做什么

    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.
    }
    

    我也发现,当使用JNI时,从C++调用java方法,如果将参数以错误的顺序传递给调用java方法,则当尝试使用调用方法中的参数时,会得到此错误(因为它们不是正确的类型)。我最初感到惊讶的是,当您调用该方法时,JNI没有为您执行这种检查,作为类签名检查的一部分,但我假设他们没有执行这种检查,因为您可能正在传递多态参数,他们必须假设您知道您在做什么

    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.
    }
    

    虽然这些答案都是正确的,但解决问题往往更容易
    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
    
    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;
    }