classpath:、file:、classpath*: 各自到哪里去找?没有前缀的路径是什么意思?jar 里的资源为什么 getFile() 失败?不同的 Resource 实现能做什么?这篇在 Spring Framework 7.0.9 上逐项验证。
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 里的同名资源;** 递归子目录。
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() 读。
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 只能读一次。