解决java.lang.IncompatibleClassChangeError

狂风中的少年 提交于 2019-12-04 05:37:32

今天整理了Maven的pom.xml文件后,把多个项目用maven集成在了一起,结果在启动Tomcat的时候,遇到一个奇葩的错误:

严重: Servlet [spring] in web application [/AbcWeb] threw load() exception
java.lang.IncompatibleClassChangeError: class org.springframework.core.type.classreading.ClassMetadataReadingVisitor has interface org.springframework.asm.ClassVisitor as super class
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2495)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:859)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1301)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1166)
	at java.lang.ClassLoader.defineClass1(Native Method)

重启Tomcat,Project>Clean都玩遍了,也没找到问题在哪里。。。于是Google之,结果从这个页面里找到了这样一段话:

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,

  5. 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).

其实,细心一点可以发现,错误描述其实很清晰了:

java.lang.IncompatibleClassChangeError: class org.springframework.core.type.classreading.ClassMetadataReadingVisitor has interface org.springframework.asm.ClassVisitor as super class

意思是说,有个叫ClassMetadataReadingVisitor的类,以一个叫ClassVisitor的接口作为父类了。但是大家都知道,Java中类和接口的关系只能是实现,而不是继承。那么为什么会出现这个类呢?我尝试着在Eclipse中打开这个报错的类:

可以看到,我的Workspace中可以发现两个版本(3.2.6和4.0.0)的ClassMetadataReadingVisitor类,分别打开这两个类(Maven会自动下载源代码),可以看到类的声明均为:

class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata

于是我又打开错误描述中提到的ClassVisitor这个类,结果却是这样的:

可以看到,Eclipse在Workspace中发现了3个这样的类,而且包名类名都完全一样。可以看到,在spring的3.1.4版本中,这个叫ClassVisitor的类其实是一个接口,这个接口被放在spring-asm模块中。而在3.2.6和4.0.0版本中,这个ClassVisitor就变成了一个抽象类。

发现这个差别后,我检查了我的spring-core模块的版本:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>3.2.6</version>
</dependency>

但同时我的pom.xml中还有这样一个包的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-asm</artifactId>
    <version>3.1.4.RELEASE</version>
</dependency>

正好这个3.1.4的接口就在Workspace中,于是这两个包当中都有这个ClassVisitor,删掉spring-asm模块后,错误消失,问题解决。

这个spring-asm包也不知道什么时候导入的,这就告诉我们:

  1. 在使用Maven处理依赖包的时候,一定不要随便乱添加依赖,用到的包才导入,没用到包的声明要及时删掉。

  2. 在整合多个maven项目的时候,要注意依赖包的版本,因为有些类(可能包含有的字段)在不同的版本中声明可能会不同,错误的类或字段声明将导致IncompatibleClassChangeError。


参考页面:

http://stackoverflow.com/questions/1980452/what-causes-java-lang-incompatibleclasschangeerror

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!