下面我将分两种主要场景来详细说明如何读取 WEB-INF 下的文件:

- 在传统的 Servlet / JSP / Java Web 项目中
- 在现代的 Spring Boot 项目中
在传统的 Servlet / JSP / Java Web 项目中
在这种场景下,我们通常使用 ServletContext 来获取 Web 应用的根路径,然后拼接出 WEB-INF 下文件的完整路径。
核心概念
ServletContext:代表整个 Web 应用,每个 Web 应用只有一个ServletContext对象,它提供了获取 Web 应用资源、初始化参数、请求转发等功能。getResourceAsStream(String path):这是最推荐的方法,它返回一个指向指定资源的InputStream,非常适合读取配置文件(如.properties,.xml),它会自动处理路径分隔符(),并且资源路径是相对于 Web 应用根目录的。getRealPath(String path):此方法将一个虚拟路径(相对于 Web 应用根目录)转换为服务器上的真实文件路径(一个String),你可以使用这个String来创建File对象,然后使用标准的 Java IO 进行读取。注意:在某些容器(如 WAR 部署在 JAR 中)或某些环境下,可能无法获取到真实路径,此时会返回null。
示例代码
假设你的项目结构如下:
webapp/
├── WEB-INF/
│ ├── config/
│ │ └── app.properties
│ └── lib/
└── index.jsp
使用 getResourceAsStream (推荐)
这种方法更通用,不依赖于文件是否存在于服务器的物理文件系统中。

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ConfigReaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取 ServletContext 对象
ServletContext servletContext = getServletContext();
// 2. 使用 getResourceAsStream 获取文件的输入流
// 路径是相对于 Web 根目录的,以 "/" 开头
InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/config/app.properties");
if (inputStream == null) {
resp.getWriter().write("Error: app.properties not found in WEB-INF/config/");
return;
}
// 3. 使用 Properties 类读取配置文件内容
Properties properties = new Properties();
try {
properties.load(inputStream);
String dbUrl = properties.getProperty("db.url");
String dbUser = properties.getProperty("db.user");
resp.setContentType("text/html");
resp.getWriter().write("<h1>Configuration Loaded Successfully!</h1>");
resp.getWriter().write("<p>Database URL: " + dbUrl + "</p>");
resp.getWriter().write("<p>Database User: " + dbUser + "</p>");
} catch (IOException e) {
resp.getWriter().write("Error loading properties file: " + e.getMessage());
} finally {
// 4. 关闭输入流
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
使用 getRealPath
当你确实需要操作 File 对象时(写入文件或使用需要 File 路径的库),可以使用此方法。
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class FileContentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取 ServletContext 对象
ServletContext servletContext = getServletContext();
// 2. 使用 getRealPath 获取文件的绝对路径
String realPath = servletContext.getRealPath("/WEB-INF/config/app.properties");
if (realPath == null) {
resp.getWriter().write("Error: Could not determine real path for app.properties.");
return;
}
// 3. 使用标准 Java IO 读取文件
File file = new File(realPath);
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
resp.setContentType("text/plain");
resp.getWriter().write("Content of app.properties:\n\n");
resp.getWriter().write(content.toString());
} catch (IOException e) {
resp.getWriter().write("Error reading file: " + e.getMessage());
}
}
}
在 Spring Boot 项目中
Spring Boot 简化了资源管理,它不强制要求资源放在 WEB-INF 下(因为嵌入式容器没有传统的 webapp 目录),如果你将文件放在 src/main/resources 目录下,Spring Boot 会将其打包到 classpath 的根目录中。
核心概念
classpath:在 Spring Boot 中,src/main/resources目录下的所有文件都会被放到 classpath 中。ClassPathResource:Spring 提供的类,用于从 classpath 中加载资源,这是 Spring Boot 中读取资源的标准方式。@Value:Spring 的注解,用于将配置文件中的值直接注入到字段中。@ConfigurationProperties:Spring Boot 的注解,用于将配置文件中的相关属性绑定到一个 Java 对象中。
示例代码
假设你的项目结构如下:

src/
└── main/
├── java/
│ └── com/
│ └── example/
│ └── demo/
│ └── DemoApplication.java
└── resources/
├── application.properties (全局配置)
└── config/
└── app-config.yml (自定义配置文件)
使用 ClassPathResource (最灵活)
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@Service
public class ConfigService {
public void readConfigFile() {
try {
// ClassPathResource 的路径是相对于 classpath 根目录的
ClassPathResource resource = new ClassPathResource("config/app-config.yml");
if (resource.exists()) {
// 获取输入流
InputStream inputStream = resource.getInputStream();
// 注意:Properties 通常用于 .properties 文件,.yml 文件需要用其他库如 SnakeYAML
// 这里仅作演示 IO 流的获取
System.out.println("Successfully found and opened the resource: " + resource.getFilename());
// 在这里可以使用 inputStream 读取文件内容
// 用 SnakeYAML 解析 yml 文件...
inputStream.close();
} else {
System.out.println("Resource not found!");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用 @Value 读取单个属性
这种方法适用于 application.properties 或 application.yml 文件。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class AppProperties {
// 从 application.properties 中读取属性
// 如果在 application.yml 中,格式为 app.name: My App
@Value("${app.name:DefaultAppName}")
private String appName;
@Value("${server.port:8080}")
private int serverPort;
public void printProperties() {
System.out.println("App Name: " + appName);
System.out.println("Server Port: " + serverPort);
}
}
使用 @ConfigurationProperties 绑定一组属性
这是最推荐的方式,用于结构化地管理配置。
在 application.yml 或 application.properties 中定义一组相关的属性。
application.yml
app:
config:
db-url: jdbc:mysql://localhost:3306/mydb
db-user: admin
db-password: secret
feature-enabled: true
AppConfig.java (配置类)
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app.config") // 指定属性前缀
public class AppConfig {
private String dbUrl;
private String dbUser;
private String dbPassword;
private boolean featureEnabled;
// 必须提供 getter 和 setter 方法
public String getDbUrl() { return dbUrl; }
public void setDbUrl(String dbUrl) { this.dbUrl = dbUrl; }
public String getDbUser() { return dbUser; }
public void setDbUser(String dbUser) { this.dbUser = dbUser; }
public String getDbPassword() { return dbPassword; }
public void setDbPassword(String dbPassword) { this.dbPassword = dbPassword; }
public boolean isFeatureEnabled() { return featureEnabled; }
public void setFeatureEnabled(boolean featureEnabled) { this.featureEnabled = featureEnabled; }
@Override
public String toString() {
return "AppConfig{" +
"dbUrl='" + dbUrl + '\'' +
", dbUser='" + dbUser + '\'' +
", dbPassword='" + dbPassword + '\'' +
", featureEnabled=" + featureEnabled +
'}';
}
}
你就可以在任何 Spring管理的 Bean 中注入并使用 AppConfig 了。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyBusinessService {
private final AppConfig appConfig;
// 通过构造器注入 (推荐)
@Autowired
public MyBusinessService(AppConfig appConfig) {
this.appConfig = appConfig;
}
public void doSomething() {
System.out.println("Using config from MyBusinessService:");
System.out.println(appConfig);
if (appConfig.isFeatureEnabled()) {
System.out.println("Feature is enabled! Connecting to DB: " + appConfig.getDbUrl());
}
}
}
总结与对比
| 特性 | 传统 Servlet (ServletContext) |
Spring Boot (ClassPathResource / @Value) |
|---|---|---|
| 适用环境 | 标准 Java Web 应用 (Servlet API) | Spring Boot 应用 |
| 核心 API | ServletContext.getResourceAsStream() |
ClassPathResource.getInputStream() |
| 路径 | 相对于 Web 根目录,如 /WEB-INF/file.txt |
相对于 classpath 根目录,如 config/file.txt |
| 优点 | 标准、通用,不依赖框架 | 与 Spring 生态无缝集成,支持多种配置绑定方式,更简洁 |
| 缺点 | 代码略显冗长,需要手动处理 IO | 主要用于 Spring 环境 |
| 推荐做法 | 在 Servlet 中,优先使用 getResourceAsStream,在 Spring Boot 中,优先使用 @ConfigurationProperties 或 ClassPathResource。 |
重要提示:在 Spring Boot 中,虽然你可以把文件放在 src/main/resources/WEB-INF/ 下,但这并不特殊,它仍然只是 classpath 的一部分,Spring Boot 的最佳实践是将配置文件放在 src/main/resources/ 下,并利用其强大的自动配置和属性绑定功能。
