核心概念
在 Java Web 应用中,配置文件通常用于存储那些不希望硬编码在代码中的信息,

- 数据库连接信息
- 第三方服务的 API 密钥
- 应用程序的开关和参数
- 日志级别
Java Web 应用的配置文件主要有两种类型:
- 属性文件:
key-value格式,如config.properties。 - XML 文件:结构化数据,如
web.xml(部署描述符) 或自定义的spring-config.xml。
使用 ServletContext 读取 Web 根目录下的配置文件 (传统 Servlet 方式)
这是最基础、最直接的方式,不依赖于任何框架,适用于任何 Java Web 应用(如 Servlet + JSP)。
原理
ServletContext 是一个接口,代表了整个 Web 应用,它提供了获取 Web 应用中资源(如文件、目录)的方法。getRealPath() 可以将 Web 路径转换为服务器文件系统上的绝对路径。
步骤
-
放置配置文件:将
config.properties文件放在 Web 应用的根目录下,即WebContent或webapp目录下。
(图片来源网络,侵删)webapp/ ├── index.jsp ├── WEB-INF/ │ └── web.xml └── config.properties <-- 放在这里 -
编写代码:在 Servlet 或 JSP 中,通过
ServletContext读取文件。config.properties内容示例:db.driver=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/mydb db.username=root db.password=123456 app.name=My Web App
ReadConfigServlet.java代码示例:import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.*; import java.util.Properties; @WebServlet("/readConfig") public class ReadConfigServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取 ServletContext 对象 ServletContext servletContext = getServletContext(); // 2. 获取配置文件在服务器上的绝对路径 // "config.properties" 是相对于 webapp 根目录的路径 String realPath = servletContext.getRealPath("/config.properties"); if (realPath == null) { response.getWriter().write("Error: config.properties not found in webapp root!"); return; } // 3. 创建 Properties 对象 Properties props = new Properties(); // 4. 使用 try-with-resources 自动关闭流 try (InputStream is = new FileInputStream(realPath)) { // 5. 加载配置文件 props.load(is); // 6. 读取配置项 String dbUrl = props.getProperty("db.url"); String appName = props.getProperty("app.name"); // 7. 输出结果 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<h1>Configuration Loaded Successfully!</h1>"); out.println("<p>App Name: " + appName + "</p>"); out.println("<p>Database URL: " + dbUrl + "</p>"); } catch (IOException e) { e.printStackTrace(); response.getWriter().write("Error reading config file: " + e.getMessage()); } } }
优点
- 原生 Java 支持,不依赖任何框架。
- 路径清晰,文件在
webapp下,方便部署。
缺点
- 硬编码路径:
/config.properties写死在代码中,不够灵活。 - 可移植性差:
getRealPath()的返回值依赖于服务器的具体部署结构,在不同容器(Tomcat, Jetty)或不同部署方式(war, exploded)下可能表现不一致。 - 不适合热部署:修改文件后通常需要重启应用才能生效。
使用 ClassLoader 读取 src/main/resources 下的配置文件 (Maven/Gradle 项目标准方式)
在现代 Java 项目(使用 Maven 或 Gradle 构建)中,源代码和资源文件是分开管理的,所有非代码资源(如配置文件、图片)都放在 src/main/resources 目录下,构建工具(如 Maven)会自动将这个目录下的所有内容复制到最终应用的 WEB-INF/classes 目录中,而这个目录是 Java 类的根路径(Classpath)。

原理
ClassLoader 负责加载 Java 类,它也可以用来加载类路径(Classpath)下的资源文件,使用 ClassLoader.getResourceAsStream() 方法可以直接通过文件名(相对于 classpath 根目录)获取输入流,非常方便。
步骤
-
放置配置文件:将
config.properties放在src/main/resources目录下。src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── ReadConfigServlet.java │ └── resources/ │ └── config.properties <-- 放在这里 └── test/ -
编写代码:代码与框架无关,可以在任何地方使用。
ReadConfigWithClassLoader.java代码示例:import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ReadConfigWithClassLoader { public static void main(String[] args) { // 1. 获取 ClassLoader // Thread.currentThread().getContextClassLoader() 是最推荐的方式 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = ReadConfigWithClassLoader.class.getClassLoader(); } // 2. 使用 try-with-resources 获取输入流 // "config.properties" 是相对于 classpath 根目录的路径 try (InputStream is = classLoader.getResourceAsStream("config.properties")) { if (is == null) { System.err.println("Sorry, unable to find config.properties"); return; } Properties props = new Properties(); props.load(is); String dbUrl = props.getProperty("db.url"); String appName = props.getProperty("app.name"); System.out.println("App Name: " + appName); System.out.println("Database URL: " + dbUrl); } catch (IOException e) { e.printStackTrace(); } } }
在 Servlet 中使用
// 在 Servlet 的 doGet 或 doPost 方法中
InputStream is = getServletContext().getResourceAsStream("/WEB-INF/classes/config.properties");
// 或者更通用的
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
优点
- 标准做法:符合 Maven/Gradle 项目结构,是业界标准。
- 可移植性好:不依赖于服务器的文件系统,只依赖于 classpath,非常灵活。
- 简洁:代码更简洁,无需关心文件的具体位置。
缺点
- 如果配置文件需要修改,通常需要重新打包和部署应用,不支持热更新。
使用 Spring 框架读取配置文件
Spring 框架提供了更强大、更灵活的方式来管理配置,主要有两种方式:XML 配置和注解/Java 配置。
基于 XML 的 PropertyPlaceholderConfigurer (传统 Spring 方式)
这种方式通过一个特殊的 Bean 来加载外部的 .properties 文件,并将其中的值注入到 Spring 的其他 Bean 中。
步骤:
-
放置配置文件:同样放在
src/main/resources下。 -
配置 Spring:在
applicationContext.xml中配置PropertyPlaceholderConfigurer。applicationContext.xml示例:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1. 加载外部的 properties 文件 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:config.properties" /> </bean> <!-- 2. 定义一个 Bean,并使用配置文件中的值 --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="${db.driver}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <!-- 3. 定义另一个使用配置的 Bean --> <bean id="myService" class="com.example.MyService"> <property name="appName" value="${app.name}" /> </bean> </beans>MyService.java示例:package com.example; public class MyService { private String appName; // 必须有 setter 方法,Spring 通过反射调用 public void setAppName(String appName) { this.appName = appName; } public void printAppInfo() { System.out.println("Service is running for app: " + appName); } }
基于 @Value 注解 (现代 Spring 方式)
从 Spring 2.5 开始,@Value 注解成为注入配置值的首选方式,它更加简洁和类型安全。
步骤:
- 启用属性支持:在 Spring 配置类上使用
@PropertySource和@EnableConfigurationProperties。 - 注入值:在需要注入的 Bean 的字段或方法上使用
@Value。
SpringConfig.java 示例:
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.beans.factory.annotation.Value;
@Configuration
// 指定 properties 文件的位置
@PropertySource("classpath:config.properties")
public class SpringConfig {
// 直接将配置文件的值注入到字段中
@Value("${app.name}")
private String appName;
@Value("${db.url}")
private String dbUrl;
// 可以设置默认值,防止配置项不存在时出错
@Value("${some.optional.key:DefaultValue}")
private String optionalKey;
// 可以编写一个方法来提供这些值
public void printConfig() {
System.out.println("App Name from @Value: " + appName);
System.out.println("DB URL from @Value: " + dbUrl);
System.out.println("Optional Key: " + optionalKey);
}
}
优点
- 与 Spring IoC 容器无缝集成:配置值可以作为 Bean 的属性被自动注入。
- 解耦:代码与配置文件完全分离,修改配置无需改动代码。
- 功能强大:支持默认值、SpEL 表达式等高级特性。
- 支持多环境:可以轻松地为开发、测试、生产环境配置不同的文件。
Spring Boot 的 application.properties / application.yml
Spring Boot 极大地简化了配置管理,它不再需要手动配置 PropertyPlaceholderConfigurer,而是约定大于配置。
原理
Spring Boot 在启动时会自动在以下位置查找 application.properties 或 application.yml 文件,并按优先级加载(后面的会覆盖前面的):
classpath:/config/classpath:/file:./config/(相对于当前工作目录)file:./
步骤
-
放置配置文件:在
src/main/resources目录下创建application.properties。# application.properties spring.application.name=MySpringBootApp server.port=8081 # 自定义配置项 myapp.db.driver=com.mysql.cj.jdbc.Driver myapp.db.url=jdbc:mysql://localhost:3306/mydb myapp.db.username=root myapp.db.password=123456
-
使用
@Value注入:与 Spring 的方式完全相同。import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @Value("${spring.application.name}") private String appName; @Value("${myapp.db.url}") private String dbUrl; @GetMapping("/") public String hello() { return "Hello from " + appName + "! DB URL: " + dbUrl; } } -
使用
@ConfigurationProperties(推荐):这种方式更强大,可以将一组相关的配置绑定到一个类型安全的 Java 对象(POJO)中。AppProperties.java(POJO)import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component // 前缀 "myapp" 会与 properties 文件中的 "myapp.xxx" 匹配 @ConfigurationProperties(prefix = "myapp") public class AppProperties { private Db db = new Db(); // 必须有 getter 和 setter public Db getDb() { return db; } public void setDb(Db db) { this.db = db; } // 内部静态类,对应 "myapp.db" 前缀下的配置 public static class Db { private String driver; private String url; private String username; private String password; // ... getter and setter methods for each field ... public String getDriver() { return driver; } public void setDriver(String driver) { this.driver = driver; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }HelloController.java(使用配置对象)import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @Autowired private AppProperties appProperties; @GetMapping("/config") public String getConfig() { return "DB URL: " + appProperties.getDb().getUrl() + ", Username: " + appProperties.getDb().getUsername(); } }
Spring Boot 的优点
- 零配置:开箱即用,无需编写任何 XML 或 Java 配置类来加载属性文件。
- 类型安全:
@ConfigurationProperties提供了编译时的类型检查。 - 环境支持:通过
application-{profile}.properties文件和spring.profiles.active属性,轻松实现多环境配置。 - 外部化配置:支持命令行参数、环境变量等多种配置来源,优先级很高。
总结与最佳实践
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
ServletContext |
纯 Servlet/JSP 项目,需要读取 webapp 目录下的文件。 |
原生,不依赖框架。 | 可移植性差,路径硬编码,不适合现代项目。 |
ClassLoader |
所有 Java 项目(Maven/Gradle),读取 src/main/resources 下的文件。 |
行业标准,可移植性好,简洁。 | 不支持热更新,需重新部署。 |
Spring @Value |
Spring/Spring Boot 项目,注入少量、分散的配置值。 | 简单直接,与 Spring 容器集成。 | 对于大量配置,代码会显得零散。 |
Spring @ConfigurationProperties |
Spring/Spring Boot 项目,管理一组相关的配置项。 | 类型安全,结构化,代码整洁。 | 需要编写对应的 POJO 类。 |
Spring Boot application.properties |
Spring Boot 项目,作为首选和核心配置方式。 | 零配置,功能强大,支持多环境。 | 与 Spring Boot 强绑定。 |
最佳实践建议:
-
对于新项目,特别是使用 Spring Boot 的项目:
- 首选:将所有配置放在
src/main/resources/application.properties或application.yml中。 - 读取方式:
- 对于简单的、独立的配置项,使用
@Value。 - 对于结构化的、相关的配置项(如数据库、Redis、邮件等),强烈推荐使用
@ConfigurationProperties,因为它最类型安全、最优雅。
- 对于简单的、独立的配置项,使用
- 首选:将所有配置放在
-
对于传统的 Spring MVC 项目:
- 将配置文件放在
src/main/resources目录下。 - 使用
@PropertySource("classpath:config.properties")结合@Value或@ConfigurationProperties来读取和管理配置。
- 将配置文件放在
-
对于非 Spring 的 Java Web 项目:
- 将配置文件放在
src/main/resources目录下。 - 在代码中使用
Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties")来读取。
- 将配置文件放在
-
关于热更新:
如果配置文件需要在不重启应用的情况下更新,可以考虑使用 Spring Cloud Config 或 Apache Commons Configuration 等专门的库,但这会增加系统复杂性,对于大多数应用,重启的代价是可以接受的。
