classnotfoundexception 是 jvm 在运行时类加载器路径中未找到指定类的字节码文件,与代码拼写无关,只取决于实际 classpath 中是否存在对应 jar 或 class 文件。

ClassNotFoundException 到底是哪个类找不到
这个异常不是说“某个类名拼错了”,而是 JVM 在当前类加载器的路径里,压根没找到你 new 或 Class.forName() 那个类的字节码文件。它不关心你代码写得对不对,只认路径和 jar 包里的实际存在性。
常见错误现象:java.lang.ClassNotFoundException: com.fasterxml.jackson.databind.ObjectMapper,但你明明加了 jackson-databind 依赖——问题往往出在:它被另一个依赖排除了、版本冲突导致没拉下来、或者打包时被 Maven Shade 插件删掉了。
- 先用
mvn dependency:tree -Dverbose看真实引入链,搜关键字确认该类所在 jar 是否真在依赖树里 - 如果是 Spring Boot 项目,检查
spring-boot-maven-plugin的repackage是否把依赖打进 fat jar;没打进去的话,运行时自然找不到 - IDE 运行没问题但命令行
java -jar报错?说明 IDE 自动把 classpath 拼全了,而你没给-cp或 MANIFEST.MF 缺少Class-Path条目
Maven 里 scope=provided 导致运行时报 ClassNotFoundException
provided 的意思是“编译时需要,但运行时由容器(比如 Tomcat、JDK)提供”。一旦你把它用在非容器环境(如本地 main 方法、JUnit 测试、Spring Boot 内嵌容器),就会直接炸——因为编译通过了,但运行时 classpath 里根本没有那个 jar。
典型场景:写了个 WebFilter,依赖 servlet-api,pom 里写成 <scope>provided</scope>,然后用 mvn exec:java 启动测试类,立刻报 ClassNotFoundException: javax.servlet.Filter。
立即学习“Java免费学习笔记(深入)”;
- 本地开发调试时,临时改成
compile或删掉<scope></scope>(默认就是 compile) - 如果必须保留
provided,就改用mvn spring-boot:run启动(Spring Boot 会自动补全 servlet 相关类) - 别在
testscope 下依赖provided依赖——test 不会继承 provided,得显式再声明一遍
自定义 ClassLoader 加载类时 ClassNotFoundException 的隐性原因
当你手动调用 MyClassLoader.loadClass("xxx.Yyy"),即使 jar 在磁盘上,也可能抛 ClassNotFoundException:不是路径错,而是父委托机制没走通,或双亲委派被绕过但资源路径没配对。
比如用 URLClassLoader 加载一个外部 jar,但忘了把它的 URL 加进构造参数;或者用了 Thread.currentThread().getContextClassLoader(),而它被上游框架(如某些 RPC 框架)悄悄替换成一个只加载特定包的 loader。
- 打印
getClassLoader().getResource("xxx/Yyy.class"),返回 null 就说明 loader 根本没看到这个类路径 - 不要假设当前线程的 context classloader 和你期望的一样——尤其在异步线程、定时任务、Dubbo/Feign 回调里,它经常是
AppClassLoader或空的 - 如果要跨 loader 加载类,确保目标类的 package 路径在 loader 的
URL[]中可访问,且没被SecurityManager拦截(Java 8+ 默认无,但某些容器有)
Jar 包内嵌依赖未解压导致 ClassNotFoundException
有些工具(比如老版本 maven-assembly-plugin)会把依赖 jar 打成 zip 压缩包塞进主 jar 的 lib/ 目录下,而不是展开成 class 文件。JVM 默认只认顶层 jar 或 classpath 上的独立 jar,不认识 “jar 里的 jar”。
现象是:java -jar app.jar 启动后报 ClassNotFoundException,但用 jar -tf app.jar | grep xxx 发现对应类确实存在——只是藏在 lib/xxx.jar 里面,没被加载。
- 改用
spring-boot-maven-plugin或shade插件,它们默认会把依赖解压合并到主 jar 的BOOT-INF/classes/下 - 如果坚持用 assembly,配置
<descriptorref>jar-with-dependencies</descriptorref>,并确保<archive></archive>里设置了manifestEntries的Class-Path指向 lib 下每个 jar(注意路径是相对 jar 包根目录的) - 运行时用
java -cp "app.jar:lib/*" MainClass替代-jar,让 JVM 主动扫描 lib 下所有 jar
最常被忽略的是:你以为 Maven 依赖都进了 classpath,其实它只管编译期;运行期 classpath 是另一套逻辑,且不同启动方式(IDE / java -jar / java -cp / docker entrypoint)之间差异极大。查不到类时,第一反应不该是“是不是少加依赖”,而是“此刻 JVM 真正看到的 classpath 是什么”。










