因为需要用到了外部jar中的Class,却在使用时遇到了NoClassDefFoundError,不过最终解决了,留下一个笔记供以后参考。 首先分析一下ClassNoDefFoundError异常,平时熟知的是ClassNotFoundException。ClassNotFoundException是找不到类的异常,那NoClassDefFoundError呢?其实也是找不到类,不过两者有着原理性的区别。
ClassNotFoundException
为类在加载时发现的异常,一般是因为要使用的类在ClassLoader中(由ClassLoder.getSystemClassLoader()获得)找不到,一般是依赖未在classpath中造成。
NoClassDefFoundError
此错误继承自 java.lang.LinkageError,是java.lang.Error类的派生类。含义是使用Class时JVM无法正确获得Class的实例数据(定义的方法、父类、接口、字段等成员)。两者区别在于前者在加载类时找不到类,是属于编译时就可能发现的异常。后者是类加载成功后但无法使用。
现实问题
笔者遇到的问题呢就与这两个异常有关。先说明一下是如何使用的吧。 首先由于需要在动态使用一堆jar包中的Class,于是使用URLClassLoader把对应的Jar包全部包含。然后使用这个classLoader动态加载类来使用(使用Reflections查找被某些注解过的类)。 由于Class就存在于diy的ClassLoader的范围,Class加载成功,getCanonicalName()获得类全路径正常。然后继续去读取Class中定义的字段、方法中被部分注解过的进行筛选。就在getDeclaredFields()或getDeclaredMethods()方法被调用时却抛出NoClassDefFoundError。而异常中的类却是这个Class内被引用过的类(暂叫DependencyClass),但DependencyClass在diy的ClassLoader的范围,单独加载也能成功加载。
问题分析
继承查看异常信息,原来是NoClassDefFoundError中又包裹了一层ClassNotFoundException。 观其异常调用堆栈是在使用当前调用类的ClassLoader(sun.reflect.Reflection.getCallerClass()加载的ClassLoader)来加载Class的依赖。但当前调用类的ClassLoader并不存在DependencyClass。
解决
找到原因问题就快解决了,因为是调用类所在ClassLoader找不到依赖的Class,那可以进行几种方式解决:
- 在工程的依赖中添加找不到类的依赖,这样做会增加依赖。
- 在调用类之前获得系统ClassLoader,将找不到类的jar添加到ClassLoader的依赖中(AppClassLader继承于URLClassLoader可通过反射修改ClassLoader来添加Jar)这种方法的依赖jar只能添加不能删减,可能影响系统稳定性) 具体方法见 https://blog.youkuaiyun.com/fd2025/article/details/80538468
- 将方法代码写成工具,与diy的ClassLoader中的jar一起加载,通过反射+反射方式实现。
以上内容纯属个人经验,不对之处欢迎来怼。

本文围绕Java中ClassNotFoundException和NoClassDefFoundError展开。介绍了两者原理性区别,前者是加载类时找不到,后者是类加载成功后无法使用。作者分享了动态使用jar包中Class时遇到NoClassDefFoundError的问题,分析原因并给出三种解决方式。
4393

被折叠的 条评论
为什么被折叠?



