推荐用 getResourceAsStream() 读取 classpath 配置,显式指定 UTF-8 编码并用 try-with-resources 关流;Spring 项目优先用 @ConfigurationProperties;非 Spring 场景需动态刷新时选 Apache Commons Configuration2 或 Nacos。

Java 项目里读配置文件,不推荐手写 Properties 加 FileInputStream——容易漏关流、路径错、编码乱、无法热更新,更别说多环境切换。真正可用的方案得看场景:简单单体用 java.util.Properties 就够;Spring 项目直接走 @Value 或 @ConfigurationProperties;微服务或需动态刷新的,得上 Apache Commons Configuration2 或 Nacos 这类外部配置中心。
用 Properties 读取 classpath 下的 application.properties
这是最轻量、无依赖的方式,适用于无框架的工具类或小型脚本。关键点不是“能不能读”,而是“怎么读才不出错”:
-
getResourceAsStream()必须用,不能用new FileInputStream("xxx.properties")—— 后者依赖当前工作目录,打包成 jar 后必然FileNotFoundException - 显式指定
UTF-8编码,否则 Windows 下中文会变??? - 别忘了
try-with-resources,否则流不关可能引发句柄泄漏(尤其在频繁 reload 场景)
Properties props = new Properties();
try (InputStream is = MyClass.class.getResourceAsStream("/application.properties")) {
if (is == null) {
throw new RuntimeException("application.properties not found in classpath");
}
props.load(is);
} catch (IOException e) {
throw new RuntimeException("Failed to load properties", e);
}
String dbUrl = props.getProperty("db.url");
Spring Boot 中用 @ConfigurationProperties 绑定类型安全配置
比 @Value 更健壮:支持嵌套对象、校验、松散绑定(my-db-url 自动映射到 myDbUrl),且能统一管理前缀。但容易踩两个坑:
- 必须在配置类上加
@ConfigurationProperties(prefix = "app"),同时该类要被 Spring 扫描到(加@Component或用@EnableConfigurationProperties) - 若配置项含特殊字符(如
app.redis.password=abc@123),需用单引号包裹,否则@会被 Spring 解析为占位符 - 不支持自动刷新(除非配合
@RefreshScope+ Spring Cloud Config / Nacos)
@Component
@ConfigurationProperties(prefix = "app.db")
public class DatabaseProperties {
private String url;
private String username;
private String password;
// getter/setter
}
用 Apache Commons Configuration2 支持多格式+层级+自动重载
当项目既不是 Spring Boot,又需要读 XML、YAML、JSON 或监听文件变化时,commons-configuration2 是最成熟的选择。它把不同格式抽象成统一接口,但要注意:
立即学习“Java免费学习笔记(深入)”;
- YAML 支持需额外引入
snakeyaml,否则抛NoClassDefFoundError - 自动重载靠
FileChangedReloadingStrategy,但它只检测最后修改时间,不感知内容变更(比如改完立刻保存两次,第二次可能被忽略) - 层级配置(如
database.host)默认启用,但若用PropertiesConfiguration,需手动调用setListDelimiterHandler(new DefaultListDelimiterHandler(','))处理数组
FileBasedConfigurationBuilderbuilder = new FileBasedConfigurationBuilder<>(PropertiesConfiguration.class) .configure(new Parameters().properties() .setFileName("config.properties") .setReloadingStrategy(new FileChangedReloadingStrategy())); PropertiesConfiguration config = builder.getConfiguration(); String host = config.getString("database.host");
避免硬编码路径和重复解析的常见错误
很多团队在工具类里反复写 new Properties().load(...),结果是:同一配置被多次解析、内存占用上升、修改后不生效。根本解法就一条:
- 所有配置加载逻辑收口到一个单例工厂类,内部用
ConcurrentHashMap缓存已解析的Properties实例,key 为文件路径(绝对路径 or classpath 资源名) - 禁止在循环或高频方法里调用配置读取——哪怕只是
getProperty,也应提前提取并复用变量 - 日志中打印配置值时,避免直接拼接敏感字段(如密码),用
***掩码处理,否则可能泄露到日志系统
路径问题永远是最隐蔽的瓶颈:IDE 里跑得好好的,一打包成 jar 就找不到文件;本地用反斜杠路径能过,Linux 服务器直接报空指针。别信“路径应该没问题”,每次部署前用 ClassLoader.getSystemResource() 打印下实际加载的 URL,比猜强十倍。










