getResourceAsStream 是 Java 載入類路徑資源(配置檔、模板、證書等)的核心 API。其行為取決於調用者(Class、ClassLoader、ServletContext)與路徑前綴(是否以 / 開頭)。本文系統梳理三大場景差異,避免「找不到資源」的常見坑。
| 調用方式 | 路徑不以 / 開頭 | 路徑以 / 開頭 | 典型用途 |
|---|---|---|---|
Class.getResourceAsStream(path) | 相對於該類所在包目錄com/pkg/Resource.txt | 相對於 ClassPath 根目錄/com/pkg/Resource.txt | 模組內部資源,隨 JAR 打包 |
ClassLoader.getResourceAsStream(path) | 相對於 ClassPath 根目錄com/pkg/Resource.txt(不可以 / 開頭) | 不支援,拋異常或返回 null | 跨模組共享資源、插件系統 |
ServletContext.getResourceAsStream(path) | 相對於 Web 應用根目錄(webapp/) | 相對於 Web 應用根目錄(/webapp/) | Web 專案靜態資源、JSP、配置檔 |
package com.example.demo;
import java.io.InputStream;
import java.io.IOException;
public class ResourceLoaderDemo {
// 專案結構假設:
// src/main/resources/
// ├── config.properties (ClassPath 根)
// └── com/example/demo/
// ├── local.properties (同包資源)
// └── sub/
// └── nested.properties (子包資源)
public static void main(String[] args) throws IOException {
// 1. Class 相對路徑(同包)
try (InputStream is = ResourceLoaderDemo.class.getResourceAsStream("local.properties")) {
printStream("Class 相對路徑", is);
}
// 2. Class 絕對路徑(ClassPath 根)
try (InputStream is = ResourceLoaderDemo.class.getResourceAsStream("/config.properties")) {
printStream("Class 絕對路徑", is);
}
// 3. ClassLoader 路徑(不可以 / 開頭)
try (InputStream is = ResourceLoaderDemo.class.getClassLoader()
.getResourceAsStream("config.properties")) {
printStream("ClassLoader 路徑", is);
}
// 4. 子包資源
try (InputStream is = ResourceLoaderDemo.class.getResourceAsStream("sub/nested.properties")) {
printStream("Class 子包相對路徑", is);
}
try (InputStream is = ResourceLoaderDemo.class.getClassLoader()
.getResourceAsStream("com/example/demo/sub/nested.properties")) {
printStream("ClassLoader 完整路徑", is);
}
}
private static void printStream(String label, InputStream is) {
System.out.printf("%-30s : %s%n", label, is != null ? "找到" : "null (未找到)");
if (is != null) {
// 實際讀取內容驗證
try (java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A")) {
System.out.println(" 內容預覽: " + s.next().substring(0, Math.min(50, s.next().length())));
} catch (Exception e) { /* ignore */ }
}
}
}
cl.getResourceAsStream("/config.properties") → 永遠返回 null。修正:移除前綴 /。src/main/resources。檢查:解壓 JAR 確認檔案存在:jar tf app.jar | grep config。Thread.currentThread().getContextClassLoader() 或 Spring 的 ResourceLoader。webapp/WEB-INF/classes 雖在 ClassPath,但 ServletContext.getResourceAsStream 基準是 webapp/。區分:JSP/靜態檔用 ServletContext,Java 代碼內部配置用 Class/ClassLoader。Spring 封裝了統一的 Resource 介面,屏蔽底層差異:
@Autowired
private ResourceLoader resourceLoader;
public void loadConfig() throws IOException {
// classpath: 前綴 -> ClassPathResource
Resource res = resourceLoader.getResource("classpath:config.properties");
// file: 前綴 -> FileSystemResource
// http: 前綴 -> UrlResource
// 無前綴 -> 依賴 ApplicationContext 類型(ServletContextResource 或 ClassPathResource)
try (InputStream is = res.getInputStream()) {
// 讀取配置
}
}
Class 相對不帶斜槓,同包資源最方便;
Class 絕對帶斜槓,ClassPath 根來尋覓。
ClassLoader 不帶斜槓,ClassPath 根統一入口;
ServletContext 管 Web 根,靜態 JSP 它最懂。