从基础到实战全解析Java加载Properties文件的六种方式
作者:醉风塘
在Java开发中,Properties文件是最常用的配置文件格式之一,其以
key=value的键值对结构存储配置信息(如数据库连接参数、接口地址、系统开关等),具有简洁易维护、解耦配置与代码的核心优势。不同场景下(如普通Java项目、Spring项目、Web应用、Java 9+模块化项目),Properties文件的加载方式存在差异。本文将详细拆解六种主流加载方式,包含适用场景、实现步骤、代码示例、优缺点及最佳实践,帮助开发者灵活应对各类配置加载需求。
一、核心前提:Properties文件基础
1. 文件格式与规范
- 后缀名:
.properties(默认)或.config(自定义) - 编码:默认
ISO-8859-1(不支持中文),推荐显式指定UTF-8 - 内容格式:
key=value(等号可替换为:),注释以#或!开头 - 示例(
config.properties):
# 数据库配置 db.url=jdbc:mysql://localhost:3306/test db.username=root db.password=123456 # 系统配置(中文需UTF-8编码) system.name=测试系统
2. 核心API
Java通过java.util.Properties类操作Properties文件,核心方法:
load(InputStream inStream):从字节流加载配置(默认ISO-8859-1编码)load(Reader reader):从字符流加载配置(支持指定编码,如UTF-8)getProperty(String key):获取指定key的value(无值时返回null)getProperty(String key, String defaultValue):获取value,无值时返回默认值
二、六种加载方式详解
方式一:ClassLoader.getResourceAsStream()(类路径加载,最常用)
适用场景
- 普通Java项目、Spring Boot项目
- 配置文件放在
classpath下(如src/main/resources目录) - 需跨环境(开发/测试/生产)移植,无需关心文件系统绝对路径
核心原理
通过类加载器(ClassLoader)读取classpath下的资源文件,路径以classpath根目录为基准,无需写绝对路径,支持打包后(JAR/WAR)的资源加载。
实现步骤与代码示例
- 配置文件位置:
src/main/resources/config.properties(Maven/Gradle项目标准目录) - 加载代码(支持三种ClassLoader获取方式):
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
public class PropertiesLoader1 {
public static void main(String[] args) {
Properties props = new Properties();
// 方式1:当前类的ClassLoader(推荐,避免线程上下文类加载器问题)
try (InputStreamReader isr = new InputStreamReader(
PropertiesLoader1.class.getClassLoader().getResourceAsStream("config.properties"),
StandardCharsets.UTF_8)) { // 显式指定UTF-8编码,解决中文乱码
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("加载配置文件失败", e);
}
// 方式2:Thread上下文类加载器(适用于多线程/框架场景)
/*try (InputStreamReader isr = new InputStreamReader(
Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"),
StandardCharsets.UTF_8)) {
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("加载配置文件失败", e);
}*/
// 方式3:系统类加载器(与方式1效果一致,不推荐)
/*try (InputStreamReader isr = new InputStreamReader(
ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties"),
StandardCharsets.UTF_8)) {
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("加载配置文件失败", e);
}*/
// 读取配置
System.out.println("数据库URL:" + props.getProperty("db.url"));
System.out.println("系统名称:" + props.getProperty("system.name")); // 中文正常显示
}
}路径规则
- 相对路径:直接写文件名(如
"config.properties"),默认从classpath根目录查找 - 子目录:若文件在
src/main/resources/config/db.properties,路径为"config/db.properties" - 禁止写绝对路径(如
"D:/config.properties"),类加载器仅识别classpath下的资源
优缺点
- 优点:跨环境移植性强、支持JAR包内资源、无需关心文件系统路径
- 缺点:仅能加载
classpath下的文件,无法加载外部文件系统的配置
方式二:FileInputStream(文件系统路径加载)
适用场景
- 配置文件放在项目外部(如服务器特定目录、用户目录)
- 需动态指定配置文件路径(如通过命令行参数传入)
- 非
classpath下的本地文件加载
核心原理
通过文件系统的绝对路径或相对路径,直接读取文件字节流,依赖操作系统的文件系统接口。
实现步骤与代码示例
- 配置文件位置:
D:/config/config.properties(绝对路径)或./config.properties(项目根目录相对路径) - 加载代码:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
public class PropertiesLoader2 {
public static void main(String[] args) {
Properties props = new Properties();
String filePath;
// 方式1:绝对路径(Windows/Dinux通用,Linux路径如"/opt/config/config.properties")
filePath = "D:/config/config.properties";
// 方式2:相对路径(基准目录为项目根目录,如IDEA中为项目文件夹)
// filePath = "./config.properties";
// 方式3:命令行参数传入路径(灵活适配不同环境)
// if (args.length > 0) filePath = args[0];
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(filePath),
StandardCharsets.UTF_8)) { // 指定UTF-8编码
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("加载配置文件失败,路径:" + filePath, e);
}
// 读取配置
System.out.println("数据库用户名:" + props.getProperty("db.username"));
System.out.println("数据库密码:" + props.getProperty("db.password"));
}
}路径规则
- 绝对路径:包含完整的盘符(Windows)或根目录(Linux),如
"D:/config.properties"、"/opt/config.properties" - 相对路径:以当前工作目录为基准(IDEA中为项目根目录,JAR运行时为启动命令所在目录),如
"./conf/config.properties"(项目根目录下的conf文件夹) - 注意:相对路径的基准目录可能因运行环境变化(如IDE、脚本启动、容器部署),需谨慎使用
优缺点
- 优点:可加载任意位置的文件、路径灵活配置
- 缺点:移植性差(路径依赖操作系统)、需手动处理路径正确性、打包后无法加载JAR内的资源
方式三:Spring框架加载(@PropertySource + @Value)
适用场景
- Spring Framework、Spring Boot项目
- 需集成Spring的依赖注入(DI),自动注入配置属性
- 支持多配置文件加载、占位符解析、配置优先级管理
核心原理
Spring通过@PropertySource注解指定配置文件路径,由PropertySourcesPlaceholderConfigurer自动加载配置,结合@Value注解注入属性,支持classpath、文件系统路径、URL等多种资源。
实现步骤与代码示例
1. 普通Spring项目(XML配置)
<!-- applicationContext.xml -->
<context:component-scan base-package="com.example"/>
<context:property-placeholder
location="classpath:config.properties,file:D:/config/ext-config.properties"
file-encoding="UTF-8"/> <!-- 支持多文件、混合路径、指定编码 -->import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SpringConfigLoader {
// 注入配置属性,支持默认值
@Value("${db.url}")
private String dbUrl;
@Value("${db.username}")
private String dbUsername;
@Value("${system.name:默认系统}") // 无该key时返回默认值
private String systemName;
// 测试方法
public void printConfig() {
System.out.println("Spring加载 - 数据库URL:" + dbUrl);
System.out.println("Spring加载 - 系统名称:" + systemName);
}
}2. Spring Boot项目(注解驱动)
Spring Boot默认加载classpath:application.properties/application.yml,若需加载自定义文件,使用@PropertySource:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
// 加载classpath下的config.properties和外部文件
@Configuration
@PropertySource(value = {
"classpath:config.properties",
"file:D:/config/ext-config.properties"
}, encoding = "UTF-8") // 指定UTF-8编码(Spring 4.3+支持)
public class BootConfigLoader {
@Value("${db.url}")
private String dbUrl;
@Value("${db.password}")
private String dbPassword;
@Value("${system.version:1.0.0}")
private String systemVersion;
// 提供getter方法供业务使用
public String getDbUrl() {
return dbUrl;
}
public String getDbPassword() {
return dbPassword;
}
}3. Spring Boot 2.4+ 推荐方式(application.properties指定额外配置)
# application.properties spring.config.import=classpath:config.properties,file:D:/config/ext-config.properties
路径规则
classpath:前缀:加载classpath下的文件(如classpath:config.properties)file:前缀:加载文件系统路径(如file:D:/config.properties)- 无前缀:默认按
classpath查找 - 支持URL路径:如
https://config.example.com/config.properties(需网络可达)
优缺点
- 优点:集成Spring生态、支持依赖注入、自动解析占位符、多文件加载、配置优先级管理
- 缺点:依赖Spring框架、非Spring项目无法使用
方式四:Java 9+ Module资源加载(Module.getResourceAsStream())
适用场景
- Java 9及以上模块化项目(使用
module-info.java) - 需遵循模块的资源访问控制(避免非模块访问模块内资源)
- 模块化打包(JMOD/JAR)后的资源加载
核心原理
Java 9引入模块系统(Module System)后,传统ClassLoader加载资源可能受模块权限限制,需通过Module接口的getResourceAsStream()方法加载模块内的资源,支持模块间的资源访问控制。
实现步骤与代码示例
- 模块化项目结构:
src/ ├── main/ │ ├── java/ │ │ ├── com/ │ │ │ └── example/ │ │ │ └── ModuleConfigLoader.java │ │ └── module-info.java │ └── resources/ │ └── config.properties
module-info.java配置(关键:开放资源访问权限):
// 模块名:com.example.config
module com.example.config {
// 允许其他模块访问本模块的资源(可选,若仅内部使用可省略)
opens com.example to java.base;
// 导出包(若需外部模块使用加载类)
exports com.example;
}
- 加载代码:
package com.example;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
public class ModuleConfigLoader {
public static void main(String[] args) {
Properties props = new Properties();
// 方式1:通过当前类的Module加载(推荐)
try (InputStreamReader isr = new InputStreamReader(
ModuleConfigLoader.class.getModule().getResourceAsStream("config.properties"),
StandardCharsets.UTF_8)) {
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("模块内加载配置文件失败", e);
}
// 方式2:通过模块名获取Module(需模块已加载)
/*Module module = ModuleLayer.boot().findModule("com.example.config").orElseThrow();
try (InputStreamReader isr = new InputStreamReader(
module.getResourceAsStream("config.properties"),
StandardCharsets.UTF_8)) {
props.load(isr);
} catch (IOException e) {
throw new RuntimeException("加载模块资源失败", e);
}*/
// 读取配置
System.out.println("模块加载 - 数据库URL:" + props.getProperty("db.url"));
System.out.println("模块加载 - 系统名称:" + props.getProperty("system.name"));
}
}关键注意事项
- 模块内资源默认仅允许模块内部访问,若需外部模块访问,需在
module-info.java中通过opens语句开放资源所在包 - 资源路径以模块的
classpath为基准,与传统类加载器路径规则一致 - 避免使用
ClassLoader加载模块化项目的资源,可能因模块权限限制失败
优缺点
- 优点:符合Java模块化规范、资源访问可控、支持模块化打包
- 缺点:仅支持Java 9+、需额外配置
module-info.java、非模块化项目无法使用
方式五:Apache Commons Configuration(第三方库加载)
适用场景
- 需简化加载逻辑(无需手动处理流、编码)
- 支持多种配置格式(Properties、XML、JSON等)
- 需高级特性(配置刷新、变量插值、多文件合并)
核心原理
Apache Commons Configuration是Apache开源项目,提供了更强大的配置加载API,封装了流处理、编码转换、异常处理等细节,支持classpath、文件系统、URL等多种资源。
实现步骤与代码示例
- 引入依赖(Maven):
<!-- Apache Commons Configuration 2.x -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.10.1</version>
</dependency>
<!-- 依赖 Commons Lang 和 Commons Collections -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>- 加载代码:
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.ex.ConfigurationException;
public class CommonsConfigLoader {
public static void main(String[] args) {
PropertiesConfiguration config;
try {
// 方式1:加载classpath下的文件
config = new PropertiesConfiguration("config.properties");
// 方式2:加载文件系统路径
// config = new PropertiesConfiguration("D:/config/config.properties");
// 方式3:指定编码(默认UTF-8,无需额外处理中文)
// config = new PropertiesConfiguration();
// config.setEncoding("UTF-8");
// config.load("classpath:config.properties");
} catch (ConfigurationException e) {
throw new RuntimeException("加载配置文件失败", e);
}
// 读取配置(支持默认值、类型转换)
String dbUrl = config.getString("db.url");
String dbUsername = config.getString("db.username");
String systemName = config.getString("system.name", "默认系统"); // 默认值
int dbPort = config.getInt("db.port", 3306); // 类型转换+默认值
System.out.println("Commons加载 - 数据库URL:" + dbUrl);
System.out.println("Commons加载 - 数据库端口:" + dbPort);
System.out.println("Commons加载 - 系统名称:" + systemName);
}
}核心特性
- 自动处理编码(默认UTF-8),无需手动创建
InputStreamReader - 支持类型转换(
getInt、getBoolean等) - 支持配置刷新(
config.refresh()) - 支持多文件合并(
CompositeConfiguration) - 支持变量插值(如
key=${other.key})
优缺点
- 优点:API简洁、功能强大、支持多种格式、无需手动处理流
- 缺点:需引入第三方依赖、增加项目体积
方式六:ServletContext.getResourceAsStream()(Web应用专属)
适用场景
- Java Web应用(Servlet、JSP、SSM等)
- 配置文件放在
WEB-INF目录下(安全,不允许客户端直接访问) - 需通过Servlet上下文加载Web应用内的资源
核心原理
Web应用启动时,Servlet容器(Tomcat、Jetty)会创建ServletContext(Servlet上下文),通过其getResourceAsStream()方法可加载Web应用目录下的资源,路径以/开头,基准目录为Web应用根目录(WEB-INF的上级目录)。
实现步骤与代码示例
- 配置文件位置:
WEB-INF/config/config.properties(Web应用目录) - 加载代码(Servlet中):
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
@WebServlet("/configLoader")
public class ServletConfigLoader extends HttpServlet {
private Properties props;
// 初始化时加载配置(Servlet生命周期)
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
props = new Properties();
// 通过ServletContext获取资源流,路径以"/"开头(基准为Web应用根目录)
String resourcePath = "/WEB-INF/config/config.properties";
try (InputStreamReader isr = new InputStreamReader(
getServletContext().getResourceAsStream(resourcePath),
StandardCharsets.UTF_8)) {
props.load(isr);
} catch (IOException e) {
throw new ServletException("Web应用加载配置文件失败,路径:" + resourcePath, e);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
// 读取配置并响应
String dbUrl = props.getProperty("db.url");
String systemName = props.getProperty("system.name");
resp.getWriter().write("Web加载 - 数据库URL:" + dbUrl + "\n");
resp.getWriter().write("Web加载 - 系统名称:" + systemName);
}
}路径规则
- 路径以
/开头:基准目录为Web应用根目录(如Tomcat的webapps/应用名/) - 示例路径:
/WEB-INF/config.properties:WEB-INF目录下的配置文件/resources/config.properties:Web应用根目录下的resources文件夹
- 禁止写绝对路径,仅支持Web应用内的相对路径
优缺点
- 优点:安全(
WEB-INF目录不暴露给客户端)、适配Web应用目录结构 - 缺点:仅支持Web应用、依赖Servlet容器、非Web项目无法使用
三、六种加载方式对比总结
| 加载方式 | 适用场景 | 核心优点 | 核心缺点 |
|---|---|---|---|
| ClassLoader.getResourceAsStream() | 普通Java项目、Spring Boot项目 | 跨环境移植性强、支持JAR内资源、无需路径依赖 | 仅加载classpath下文件 |
| FileInputStream | 外部配置文件、动态指定路径 | 可加载任意位置文件、路径灵活 | 移植性差、需手动处理路径正确性 |
| Spring框架(@PropertySource) | Spring生态项目 | 支持DI注入、占位符解析、多文件加载 | 依赖Spring框架 |
| Java 9+ Module加载 | 模块化项目(Java 9+) | 资源访问可控、符合模块化规范 | 仅支持Java 9+、需配置module-info.java |
| Apache Commons Configuration | 需高级配置特性(多格式、刷新) | API简洁、功能强大、自动处理编码 | 需引入第三方依赖 |
| ServletContext.getResourceAsStream() | Web应用 | 安全(WEB-INF)、适配Web目录结构 | 仅支持Web应用、依赖Servlet容器 |
四、最佳实践
1. 配置文件存放位置
- 普通Java项目:
src/main/resources(classpath根目录),子目录按功能划分(如resources/config/db.properties) - Web应用:
WEB-INF/config(避免客户端直接访问) - 外部配置:生产环境建议放在服务器独立目录(如
/opt/config),通过环境变量或命令行参数指定路径
2. 编码处理规范
- 强制指定UTF-8编码:使用
InputStreamReader(原生API)或框架自带的编码配置(Spring、Commons Configuration),避免中文乱码 - 禁止在Properties文件中直接写中文(即使指定UTF-8,部分工具可能不兼容),推荐使用Unicode编码(如
system.name=\u6d4b\u8bd5\u7cfb\u7edf)
3. 路径写法规范
- 优先使用
classpath:前缀(Spring项目)或相对classpath路径(ClassLoader方式),避免绝对路径 - 多环境适配:通过环境变量(如
${CONFIG_PATH})或配置中心(Nacos、Apollo)管理路径,避免硬编码
4. 避免硬编码
- 禁止在代码中写死配置文件路径(如
"D:/config.properties"),通过命令行参数、环境变量、Spring Profiles等方式动态指定 - 示例(Spring Boot多环境):
# application-dev.properties(开发环境) config.path=classpath:config-dev.properties # application-prod.properties(生产环境) config.path=file:/opt/config/config-prod.properties
5. 配置刷新机制
- 静态配置:加载一次即可(如数据库连接参数)
- 动态配置:需支持刷新(如通过Apache Commons Configuration的
refresh()方法,或配置中心推送更新)
五、常见问题排查
| 问题现象 | 根因分析 | 解决方案 |
|---|---|---|
| 中文乱码 | 未指定UTF-8编码,默认ISO-8859-1不支持中文 | 使用InputStreamReader指定UTF-8,或框架编码配置 |
| 找不到配置文件(FileNotFoundException) | 路径错误(绝对路径不存在、相对路径基准错误) | 检查路径正确性、使用绝对路径测试、打印System.getProperty("user.dir")确认基准目录 |
| ClassLoader加载不到文件 | 文件未放在classpath下、路径写绝对路径 | 移至src/main/resources、使用相对classpath路径 |
| Spring @Value注入为null | 未加@PropertySource、配置文件路径错误、未扫描组件 | 检查注解配置、路径正确性、@Component扫描范围 |
| 模块化项目加载失败 | 模块未开放资源访问权限 | 在module-info.java中添加opens 包名 to 模块名 |
六、总结
Java加载Properties文件的六种方式各有适配场景,核心选择原则:
- 普通Java项目/ Spring Boot项目:优先使用
ClassLoader.getResourceAsStream()或Spring的@PropertySource - 外部配置文件/动态路径:使用
FileInputStream或命令行参数+classpath混合方式 - Web应用:使用
ServletContext.getResourceAsStream() - Java 9+模块化项目:使用
Module.getResourceAsStream() - 需高级特性:使用Apache Commons Configuration
实际开发中,需结合项目类型(普通Java、Spring、Web、模块化)、配置存放位置(classpath内/外)、功能需求(动态刷新、多环境)选择合适的加载方式,并遵循编码规范、路径规范、避免硬编码等最佳实践,确保配置加载的可靠性和可维护性。
到此这篇关于Java加载Properties文件的六种方式:从基础到实战全解析的文章就介绍到这了,更多相关Java加载Properties文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
