jarurlconnection读取资源时路径以/开头会失败,因classloader将其视为绝对路径而jar内无文件系统根目录;应始终用相对路径如getresource("meta-inf/manifest.mf")。

JarURLConnection 读取资源时路径以 / 开头会失败
Java 用 JarURLConnection 加载 jar 包内资源,最常踩的坑是路径写成 /META-INF/MANIFEST.MF —— 这种带前导斜杠的写法在 IDE 里可能“碰巧”能跑,但打成 jar 后必然 FileNotFoundException。因为 ClassLoader.getResource() 和底层 JarURLConnection 解析路径时,把开头的 / 当作绝对路径处理,而 jar 内部资源没有文件系统意义上的“根目录”。
正确做法始终用相对路径:
-
getResource("META-INF/MANIFEST.MF")✅ -
getResource("com/example/config.json")✅ -
getResource("/com/example/config.json")❌(多了一个/)
getResourceAsStream() 比 openConnection() 更安全、更常用
很多人想手动构造 JarURLConnection:先 url.openConnection(),再强转成 JarURLConnection,再调 getJarFile()……这不仅冗余,还容易因 URL 协议不是 jar: 而抛 ClassCastException(比如在开发环境走的是 file: 协议)。
绝大多数场景根本不需要碰 JarURLConnection:
立即学习“Java免费学习笔记(深入)”;
- 直接用
getClass().getResourceAsStream("xxx.txt"),它自动适配 jar 内/外两种路径 - 如果需要
JarFile实例(比如遍历所有 entry),才需检查协议:if ("jar".equals(url.getProtocol())),再安全转型 - 避免对
url.openConnection()返回值做无条件强转
路径中的空格和特殊字符会让 JarURLConnection 解析失败
资源名含中文、空格或括号(如 config v2.json)时,getResource() 返回的 URL 已被 URL 编码(变成 config%20v2.json),但某些老版本 JDK 的 JarURLConnection 在解析 encoded path 时会出错,表现为 IOException: invalid manifest format 或空流。
稳妥解法是让路径“干净”:
- 资源文件名只用 ASCII 字母、数字、下划线、短横线(
config_v2.json) - 避免在路径中出现
%、+、空格、中文等 - 若必须支持,改用
Thread.currentThread().getContextClassLoader().getResource()+ 手动 decode(但多数项目没必要这么复杂)
Spring Boot fat jar 下 jar:file: 和 jar:!/ 协议行为不一致
Spring Boot 打包的 fat jar(含嵌套 jar)会让 URL 变成 jar:file:/app.jar!/BOOT-INF/lib/xxx.jar!/application.yml。这时 new URL(...).openConnection() 返回的不再是标准 JarURLConnection,而是 Spring 自己的 Handler 实现,getJarFile() 会抛 IllegalStateException。
别试图在这种 URL 上硬套原生 JarURLConnection 逻辑:
- 优先用
Resource抽象(ClassPathResource/UrlResource),Spring 会自动处理嵌套 - 如果非要用原生 API,先判断 URL 是否含
!/,再用java.util.jar.JarFile手动打开外层 jar,再用getJarEntry()提取嵌套路径 - 注意:JDK 9+ 对 multi-release jar 和 nested jar 支持仍有限,不要依赖
JarURLConnection的内部字段
真正难的从来不是怎么写路径,而是当不同 classloader、不同打包方式、不同 JDK 版本混在一起时,那个看似简单的 getResource() 背后到底走了哪条链路——这时候日志里多打一行 url.toString(),比查十页文档都管用。










