java.lang.IncompatibleClassChangeError,Found class * but interface was expected

如我遇到的问题是:java.lang.IncompatibleClassChangeError: Found class com.**.redis.RedisService, but interface was expected.
其实很明显的告诉了我需要一个RedisService Interface类,但RedisService确是Class. 所以发生了这个报错。

(出现这种问题,八成的原因是因为依赖版本冲突,当然我这个也是,只不过我遇到的是我自己写的依赖包。)

建议多去stackoverflow逛逛, 答案质量比较高,下面是stackoverflow的回答,看完这个我相信你就有答案了。
https://stackoverflow.com/questions/1980452/what-causes-java-lang-incompatibleclasschangeerror

贴出其中给一部分:

PS: 自己翻译吧, 当遇到未知问题还是要有一定的翻译能力

Your newly packaged library is not backward binary compatible (BC) with old version. For this reason some of the library clients that are not recompiled may throw the exception.

This is a complete list of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeError if they run on a new one (i.e. breaking BC):

1. Non-final field become static,
2. Non-constant field become non-static,
3. Class become interface,
4. Interface become class,
if you add a new field to class/interface (or add new super-class/super-interface) then a static field from a super-interface of a client class C may hide an added field (with the same name) inherited from the super-class of C (very rare case).
Note: There are many other exceptions caused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError and AbstractMethodError.

The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility" written by Jim des Rivières.

其中链接: Evolving Java-based APIs

排查方式

这里针对我遇到的异常来做个展示,首先要做的当然是先解决依赖冲突的问题(说不定解决完你的问题就解决了),这里不演示。

  1. 首先根据异常定位到具体的代码位置,找到对应的包
    at com.apicrypto.util.NeedCryptoUtils.getEncryptKey(NeedCryptoUtils.java:113)
java.lang.IncompatibleClassChangeError: Found class com.redis.RedisService, but interface was expected
at com.apicrypto.util.NeedCryptoUtils.getEncryptKey(NeedCryptoUtils.java:113)
at com.apicrypto.interceptor.DecryptFilter.doFilter(DecryptFilter.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at com.truckerpath.apicrypto.interceptor.InstallationIdValidateFilter.doFilter(InstallationIdValidateFilter.java:50)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at dev.akkinoc.spring.boot.logback.access.tomcat.LogbackAccessTomcatValve.invoke(LogbackAccessTomcatValve.kt:55)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:735)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:1583)
  1. 找到对应的位置后,解压jar包。利用javap命令查看此NeedCryptoUtils.class的字节码指令。

    javap -c -v NeedCryptoUtils.class
    找到对应的方法,
    15: invokeinterface #69, 3
    // InterfaceMethod com/redis/RedisService.get:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    这里可以看出,编译包中的字节码是invokeinterface(interface.method()指令,而当我反编译RedisService类的最新包时,编译出来是invokevirtual(class.method()),这里就和报错信息对上了Found class com.redis.RedisService, but interface was expected
    编译指令参考:https://zhuanlan.zhihu.com/p/356847335

    public static java.lang.String getEncryptKey(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
    stack=4, locals=3, args_size=1
    0: aload_0
    1: invokestatic  #59                 // Method org/apache/commons/lang3/StringUtils.isBlank:(Ljava/lang/CharSequence;)Z
    4: ifeq          9
    7: aconst_null
    8: areturn
    9: getstatic     #7                  // Field redisService:Lcom/redis/RedisService;
    12: ldc           #67                
    14: aload_0
    15: invokeinterface #69,  3           // InterfaceMethod com/redis/RedisService.get:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    20: astore_1
    21: aload_1
    22: invokestatic  #75                 // Method org/apache/commons/lang3/StringUtils.isNotBlank:(Ljava/lang/CharSequence;)Z
    25: ifeq          88
    28: getstatic     #7                  // Field redisService:Lcom/redis/RedisService;
    31: aload_0
    32: invokedynamic #78,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
    37: aload_1
    38: invokeinterface #82,  3           // InterfaceMethod com/redis/RedisService.incr:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Long;```
    
    
  2. 直接1中的包中,引用的RedisService.class版本,然后对比你项目解决完冲突后的RedisService.class版本,会发现1包中的RedisService是个public interface RedisService{}, 而最新包RedisService,却是public class RedisService{}, 尽管两个RedisService的方法都一样,使用方式也都一样,但编译指令不一样。 接下来你就保持版本一致就好了。

  3. 我的解决方案是将RedisService改为了public interface RedisService{},稍微改了一下实现方式,因为是我对RedisService做了升级,底层包增加了Redis读写分离的功能,但写的过程中以为只要方法都有,使用方式一样就可以适配老版本,没有考虑编译指令的问题。所以遇到此问题后,就将RedisService改回接口了。然后重新打包编译,解决问题。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值