
本文详解在 spring boot、tomcat 等 java 应用中,**不可直接加载“含 libs 目录的胖包(fat jar)”作为依赖库**;推荐将其重构为标准依赖,并通过 maven/gradle 或 `system` 作用域规范引入,确保类路径清晰、可维护且符合 jvm 类加载机制。
在 Java 生态中,常遇到一种结构特殊的外部 JAR:其内部不仅包含编译后的 class 文件,还自带一个 libs/ 目录,内含 d1.jar、d2.jar 等依赖项(即类似 Spring Boot 打包生成的可执行 fat jar)。这种结构本质上是为「运行」设计的,而非「被引用」。JVM 的类加载器(如 AppClassLoader)默认只扫描 -classpath 或 loader.path 指定的顶层 JAR 文件,不会自动递归解压并加载其内部嵌套的 JAR 包——这与 java -jar xxx.jar 启动时由 LaunchedURLClassLoader 特殊处理的机制完全不同。
因此,试图直接将此类 JAR 添加到 --loader.path=xxx.jar(Spring Boot)或 -cp xxx.jar(普通 Java 应用)中,会导致 ClassNotFoundException:d1 和 d2 中的类无法被发现。
✅ 正确做法是解耦:将 xxx.jar 本身作为纯净的业务/工具库(即移除 libs/ 目录,仅保留自身 class),再独立管理其依赖:
-
首选方案:发布至私有/公共仓库(推荐)
将 d1.jar、d2.jar 发布到 Nexus、Artifactory 或 Maven Central,然后在项目中声明标准依赖:com.example d1 1.2.0 com.example d2 3.4.1 -
次选方案:本地系统路径依赖(仅限开发或离线场景)
若无法上传仓库,可使用 system scope(注意:Maven 3.8.1+ 默认禁用,需配置 allowSystemScope=true):com.example d1 1.2.0 system ${project.basedir}/lib/d1.jar ⚠️ 注意:system 依赖不会参与传递性解析,且 Spring Boot 的 spring-boot-maven-plugin 默认不打包 system 依赖,需显式配置 true。
不推荐方案:手动解压 + 动态追加 classpath
虽可通过脚本解压 xxx.jar/libs/*.jar 并拼接 -cp 参数,但会破坏构建可重现性、增加部署复杂度,且易引发版本冲突或类加载顺序问题,违背 Java 依赖管理最佳实践。
? 总结:
- Fat JAR ≠ Library JAR:含 libs/ 的 JAR 是交付产物,不是可复用的依赖单元;
- 依赖应显式声明:每个 JAR 都应在构建配置中独立定义,由工具统一解析、下载、隔离;
- 构建即契约:清晰的依赖声明让团队协作、CI/CD 和安全扫描(如 OWASP Dependency-Check)更可靠。
重构 xxx.jar 为标准库后,无论是 Spring Boot 的 loader.path、Tomcat 的 shared.loader,还是普通 Java 的 -cp,都能稳定、可预期地加载所有类。










