bigywater.tech
Spring · 16

Resource 抽象:classpath:、file:、classpath*: 的区别

classpath:、file:、classpath*: 各自到哪里去找?没有前缀的路径是什么意思?jar 里的资源为什么 getFile() 失败?不同的 Resource 实现能做什么?这篇在 Spring Framework 7.0.9 上逐项验证。

1 对 3
classpath:META-INF/spring.factories 与 classpath*:META-INF/spring.factories 找到的资源个数
921 字节
jar 里的 AutoConfiguration.imports 的 contentLength(isFile() 为 false)
只读一次
InputStreamResource 第二次读抛 IllegalStateException

前缀

实测 · 本机执行输出
  classpath:conf/a.txt → ClassPathResource exists=true 内容=来自 classpath 的 conf/a.txt
  file:<绝对路径> → FileUrlResource exists=true 内容=来自文件系统
  没有前缀的 conf/a.txt(DefaultResourceLoader)→ ClassPathContextResource exists=true 内容=来自 classpath 的 conf/a.txt
  没有前缀的绝对路径 → ClassPathContextResource exists=false
  没有前缀的相对路径 other.txt(工作目录里没有这个文件)→ ClassPathContextResource exists=false
  https 前缀(只看类型,不联网)→ UrlResource
  在 ApplicationContext 里没有前缀的 conf/a.txt → ClassPathContextResource exists=true 内容=来自 classpath 的 conf/a.txt(上下文会按自己的类型决定;GenericApplicationContext 的默认是 classpath)
  在 ApplicationContext 里 file:conf/a.txt(相对工作目录)→ FileUrlResource exists=false

classpath: 与 file: 分别对应类路径与文件系统,没有前缀按 classpath 找。

通配符

实测 · 本机执行输出
  classpath:conf/*.txt                       → 1 个:out/conf/a.txt
  classpath:conf/**/*.txt                    → 2 个:out/conf/a.txt ; out/conf/sub/b.txt
  classpath:META-INF/spring.factories        → 1 个:spring-boot-autoconfigure-4.0.8.jar!.../spring.factories
  classpath*:META-INF/spring.factories       → 3 个:spring-boot-autoconfigure-4.0.8.jar!.../spring.factories ; spring-boot-4.0.8.jar!.../spring.factories ; spring-aop-7.0.9.jar!.../spring.factories
  classpath*:META-INF/spring/*.imports       → 1 个:spring-boot-autoconfigure-4.0.8.jar!.../org.springframework.boot.autoconfigure.AutoConfiguration.imports
  classpath:META-INF/spring/*.imports        → 1 个:spring-boot-autoconfigure-4.0.8.jar!.../org.springframework.boot.autoconfigure.AutoConfiguration.imports

classpath: 只取第一个匹配,classpath*: 找出所有 jar 里的同名资源;** 递归子目录。

jar 里的资源

实测 · 本机执行输出
  ClassPathResource(conf/a.txt) getFile() → out/conf/a.txt
  jar 里的资源 URL 前缀 → jar;isFile() = false;getFile() → ✗ FileNotFoundException: class path resource [META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports] cannot be resolved to absolute file path because it does not reside in the file system: <m2>;getInputStream() 正常 = true
  contentLength = 921;lastModified > 0 = true;getFilename() = org.springframework.boot.autoconfigure.AutoConfiguration.imports

不在文件系统里,getFile() 失败,用 getInputStream() 读。

Resource 的种类

实测 · 本机执行输出
  FileSystemResource 可写:写进去的;isWritable = true
  ClassPathResource 实现 WritableResource?false
  ByteArrayResource:ByteArrayResource exists=true 内容=内存里的;getFilename = null
  InputStreamResource 第一次读 3 字节;第二次读 → ✗ IllegalStateException: InputStream has already been read (possibly for early content length determination) - do not use InputStreamResource if a stream needs to be read multiple times

FileSystemResource 可写,ClassPathResource 只读,InputStreamResource 只能读一次。

分步演示

Resource 抽象
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9(classpath 上有 spring-boot、spring-boot-autoconfigure 等 jar);工作目录是一个空目录;通配符匹配的 jar 清单是这次 classpath 上有的那几个
    • 没有验证:Windows 路径;嵌套 jar(Boot 的可执行 jar,见后面的 fat-jar 篇)里 classpath*: 的行为;WebApplicationContext 里的 ServletContextResource;ResourcePatternResolver 的缓存;classpath*:com/** 这类宽通配符的扫描代价
    京ICP备2026067502号-1京公网安备11010502063487号