classnotfound异常本质是运行时类路径缺失而非编译期未引入,需用mvn dependency:tree -dincludes和-dverbose查真实依赖树,再以-java -verbose:class验证jvm实际加载的jar路径与版本。

类找不到异常(ClassNotFoundException)在 Maven 项目中,往往不是“真没引入”,而是“引入了但没加载到运行时 classpath”。关键要区分编译期可见性和运行期实际类路径——Maven 的依赖解析、作用域、排除规则和 JVM 类加载机制共同决定了最终哪个 jar 被加载。
看实际参与构建的依赖版本
用 mvn dependency:tree 查真实依赖树,别只信 pom.xml 里写的版本:
- 加
-Dincludes=groupId:artifactId快速定位目标依赖,比如:mvn dependency:tree -Dincludes=com.google.guava:guava - 加
-Dverbose显示被仲裁掉的冲突版本,例如输出中出现(omitted for conflict with 31.1-jre)就说明有更高/更低版本被忽略 - 注意 scope:test、provided、runtime 等作用域的依赖不会打到最终 jar 或 war 中,运行时自然找不到
确认运行时真正加载的是哪个 jar
编译通过不代表运行时能访问到类。得验证 JVM 启动时实际加载的路径和版本:
- 启动时加
-verbose:class参数,比如:java -verbose:class -cp target/app.jar com.example.Main,观察日志中loaded的 guava 或 jackson 所在 jar 路径 - 检查打包结果:对 jar 包执行
jar -tf target/app.jar | grep -i guava,确认该类是否真的被打进去了 - Spring Boot 项目注意 fat-jar 结构,用
jar -tvf target/app.jar | head -20看 BOOT-INF/lib 下实际包含哪些 jar
借助 IDE 插件快速识别冲突
IntelliJ IDEA 安装 Maven Helper 插件后,打开 pom.xml 底部会出现三个新 tab:
- Conflicts:直接列出所有存在多版本的依赖,标红的是被忽略的版本,白色是最终选用的版本
- All Dependencies as List:按依赖链从下往上展示,方便追踪谁引入了哪个版本
- All Dependencies as Tree:树形结构,支持搜索,适合定位某类具体来自哪条路径
检查 shade 或 assembly 是否误删/覆盖了类
如果用了 maven-shade-plugin 打 fat-jar,ClassNotFound 很可能源于重定位或过滤规则:
- 检查
<relocations></relocations>是否把包名改错了,导致运行时找不到原始类路径 - 查看
<filters></filters>是否误删了META-INF/services/或module-info.class等关键资源 - 确认
<transformers></transformers>(如 ServicesResourceTransformer)是否合并失败,造成 SPI 服务不可用











