Spring 读取配置文件,最常用的是 @Value 占位符注入:在字段上用 ${key} 把配置值直接注入,改配置不用重新编译代码。本文讲清 Spring 读取配置文件的 3 种主流方式:@Value 注入单个值、@ConfigurationProperties 批量绑定到对象、以及用 Environment 接口按需读取。你还会看到多环境切换、占位符嵌套、@Value 与 @ConfigurationProperties 怎么分工这些真实开发里必踩的点。读完你不仅能把 application.yml 里的值读进 Java 对象,还知道哪种写法该用在哪个场景。今天编程狮就把这块讲透。

一、为什么需要把配置读出来
配置文件存在的意义,是「把会变的东西和不变的逻辑分开」。数据库连接串在测试库和生产库不一样,短信接口的密钥每个月可能轮换,功能开关要能热关。如果这些写死在 Java 代码里,每次改动都得重新打包发布,既危险又低效。
Spring 读取配置文件的本质,就是从 application.properties 或 application.yml 这类外部文件里,把键值对加载进 Spring 容器的环境(Environment),再按需交到你写的 Bean 字段上。理解这一点很关键:配置先进入「环境」,再决定用哪种姿势取出来。
⚠️ 注意:配置键是区分大小写的(严格说是配置源决定的,yml/properties 默认都敏感),
app.name和app.Name是两个不同的键;另外同一个键被多处定义时,后加载的源会覆盖先加载的,排查问题时容易在这栽跟头。
另一个常见误区是「配置越多越乱」。其实把配置集中到 application.yml 并配合多环境文件,反而比散落在代码各处更好维护。想知道 Spring 整体怎么搭,Spring 框架基础教程 里有一章专门讲容器与配置体系。理清「为什么读、读什么、读到哪」这三点,后面三种方式你一看就通。
二、方法一:@Value 注入单个值(最推荐读单值)
适合谁:只需要读取一两个独立配置项,比如某个开关、某个地址。代价:配置项一多,字段上会挂满 @Value,不够优雅。
@Component
public class MailConfig {
// 读取单个配置,带默认值
@Value("${mail.host:localhost}")
private String host;
@Value("${mail.port:25}")
private int port;
// 支持 SpEL 表达式做简单运算
@Value("#{${mail.port:25} * 2}")
private int doubledPort;
}
${mail.host:localhost} 的语义是:从环境里取 mail.host,取不到就用 localhost 兜底。冒号后面跟的就是默认值,能避免配置缺失直接启动报错。如果想做点轻量计算,还可以用 #{} 包一段 SpEL 表达式。
@Value 的强项是「精准、直接」,写起来心智负担最小。但它和字段强绑定,类型转换只支持基础类型与 Spring 内置的 Converter,遇到「一组相关的配置」就会显得零散——十个邮件相关配置就得写十个 @Value。一个隐藏坑:占位符里引用了不存在的键又没给默认值,Spring 启动阶段就会抛 IllegalArgumentException 失败退出,所以生产配置务必配齐或给默认。实际项目里,@Value 常出现在读取第三方服务的单点地址、读取功能开关这种场景,简单够用。
三、方法二:@ConfigurationProperties 批量绑定(类型安全)
适合谁:一组结构相关的配置,想整体映射成一个对象。代价:需要额外写 Bean 类,比 @Value 多一层定义。
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
private String host = "localhost";
private int port = 25;
private List<String> admins = new ArrayList<>();
// getter / setter 必须存在,Spring 靠它们赋值
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public List<String> getAdmins() { return admins; }
public void setAdmins(List<String> admins) { this.admins = admins; }
}
@ConfigurationProperties(prefix = "mail") 会把所有以 mail. 开头的配置,按「松散绑定」规则(kebab-case、camelCase、下划线都能匹配)灌进这个类的字段。比起 @Value 一个个写,它把配置当成一个有结构的对象来管理,IDE 还能给你自动补全和类型校验。
这种写法的优势在「聚合与校验」。比如 admins 是一个列表,yml 里写成多行数组就能直接收进来;配合 @Validated 还能在启动时校验 port 的范围,配置错了立刻暴露而不是运行时才炸。一个常见误用是忘了加 @EnableConfigurationProperties 或没把类扫进容器,结果注入的对象字段全是 null。还有人把 getter/setter 漏写,Spring 无法赋值也会得到空值。当你面对的是「一整块配置」而非「一个值」,@ConfigurationProperties 就是首选,可读性远高于满屏 @Value。
四、方法三:Environment / @PropertySource 按需读取(最灵活)
适合谁:运行期才知道要读哪个键,或要读取自定义位置的配置文件。代价:失去编译期类型提示,取值偏手动。
@Configuration
@PropertySource("classpath:custom.properties")
public class EnvConfig {
@Autowired
private Environment env;
public String read() {
// 运行期动态取键,带默认值
String token = env.getProperty("api.token", "default-token");
// 还能按类型取,并判断是否存在
Integer timeout = env.getProperty("api.timeout", Integer.class, 3000);
boolean hasKey = env.containsProperty("api.token");
return token;
}
}
Environment 是 Spring 配置的「总入口」,所有配置源最终都汇到这里。env.getProperty 既能按字符串取,也能指定目标类型(如 Integer.class),还能用 containsProperty 先判断键存不存在,非常灵活。@PropertySource 则负责把默认环境之外的自定义文件(比如 custom.properties)也加载进来。
这种写法的强项是「动态与扩展」。比如密钥名由运行参数决定、或者你要临时从一份业务配置文件读东西,Environment 比写死字段更合适。但要注意:手动取值没有类型安全的保证,把 "abc" 当 Integer 取会得到 null 而非报错。想系统复习 Spring 配置相关的各种注解与属性源机制,Spring 配置速查手册 里按字母给你整理好了。一句话总结:@Value 和 @ConfigurationProperties 是「声明式」取配置,Environment 是「命令式」取配置,前者省心、后者自由。
五、三种方式怎么选,以及两个高频坑
日常选型就记一张「分工表」:@Value 读单个零散值最省事;@ConfigurationProperties 读一整块结构化配置最稳;Environment 在运行期动态取键、读自定义文件时最灵活。没有谁替代谁,是按「取多少、取多死板」来分工的。
- 多环境切换:生产、测试的配置不同,别用
@Value硬写死。正确做法是在application.yml里用spring.profiles.active指定环境,再配application-dev.yml、application-prod.yml这类带 profile 后缀的文件,Spring 会自动按激活的环境合并配置。键重复时 profile 专属文件优先级更高。 - 占位符嵌套:
${a:${b:default}}这种「取不到 a 就退到 b」的写法很好用,但要注意别把默认值写成另一个必须存在的业务键,否则一层套一层反而难排查。另外${}和#{}不要混写在同一处,容易触发解析顺序的诡异问题。
踩坑清单就记这两条。第一条最实用——多环境几乎是每个上线项目都绕不开的,用 profile 文件而不是改代码里的 @Value,才能做到「一套代码、多套配置」。第二条是「写法陷阱」,占位符套占位符看起来很聪明,真出问题时栈信息会让你怀疑人生。理清三者边界,Spring 读取配置文件基本不会再把你难住。

总结
Spring 读取配置文件,日常读单个值用 @Value("${key}") 最省事;读一整块结构化配置用 @ConfigurationProperties 做类型安全绑定最稳;运行期动态取键或读自定义文件,用 Environment 接口最灵活。要点带走:
@Value支持${key:默认值}兜底,缺配置也不会启动失败;@ConfigurationProperties靠 prefix 批量绑定,必须保留 getter/setter;- 多环境用
spring.profiles.active配 profile 文件,别把配置写死在代码里。
延伸学习
想把这块知识系统补齐,可以按这个顺序来:
- Spring 入门课程;
- 想看另一种讲法,参考 Spring 读取配置笔记 的实战思路;
- 写完想立刻试,在线运行 Java 代码 免安装跑片段。
常见问题
Q:@Value 读取的配置项不存在会怎样?
A:如果只写 ${mail.host} 而没有默认值,且配置文件里确实没有这个键,Spring 在启动阶段就会抛 IllegalArgumentException 导致应用起不来。正确做法是用 ${mail.host:默认值} 给定兜底值,或者直接保证配置文件里该键一定存在,避免启动即失败。
Q:@Value 和 @ConfigurationProperties 该选哪个?
A:看配置的「粒度」。零散的一两个值(如功能开关、单点地址)用 @Value 最直接;一组相关的结构化配置(如邮件服务的主机、端口、管理员列表)用 @ConfigurationProperties 批量绑定更清晰,还带类型安全和校验。两者不冲突,可以共存于同一项目。
Q:多套环境(开发/测试/生产)的配置怎么管理?
A:不要改代码里的 @Value。在 application.yml 中用 spring.profiles.active 指定当前环境,再分别提供 application-dev.yml、application-test.yml、application-prod.yml 等 profile 专属文件,Spring 会按激活的环境自动合并,键重复时专属文件优先级更高。

免费 AI IDE



