Spring 读取配置文件怎么读?附 3 种常见方式一次讲清

编程狮(w3cschool.cn) 2026-09-03 18:24:34 浏览数 (16)
反馈

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

Spring 读取配置方法图

一、为什么需要把配置读出来

配置文件存在的意义,是「把会变的东西和不变的逻辑分开」。数据库连接串在测试库和生产库不一样,短信接口的密钥每个月可能轮换,功能开关要能热关。如果这些写死在 Java 代码里,每次改动都得重新打包发布,既危险又低效。

Spring 读取配置文件的本质,就是从 application.propertiesapplication.yml 这类外部文件里,把键值对加载进 Spring 容器的环境(Environment),再按需交到你写的 Bean 字段上。理解这一点很关键:配置先进入「环境」,再决定用哪种姿势取出来。

⚠️ 注意:配置键是区分大小写的(严格说是配置源决定的,yml/properties 默认都敏感),app.nameapp.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.ymlapplication-prod.yml 这类带 profile 后缀的文件,Spring 会自动按激活的环境合并配置。键重复时 profile 专属文件优先级更高。
  • 占位符嵌套${a:${b:default}} 这种「取不到 a 就退到 b」的写法很好用,但要注意别把默认值写成另一个必须存在的业务键,否则一层套一层反而难排查。另外 ${}#{} 不要混写在同一处,容易触发解析顺序的诡异问题。

踩坑清单就记这两条。第一条最实用——多环境几乎是每个上线项目都绕不开的,用 profile 文件而不是改代码里的 @Value,才能做到「一套代码、多套配置」。第二条是「写法陷阱」,占位符套占位符看起来很聪明,真出问题时栈信息会让你怀疑人生。理清三者边界,Spring 读取配置文件基本不会再把你难住。

Spring 读取配置怎么选

总结

Spring 读取配置文件,日常读单个值用 @Value("${key}") 最省事;读一整块结构化配置用 @ConfigurationProperties 做类型安全绑定最稳;运行期动态取键或读自定义文件,用 Environment 接口最灵活。要点带走:

  • @Value 支持 ${key:默认值} 兜底,缺配置也不会启动失败;
  • @ConfigurationProperties 靠 prefix 批量绑定,必须保留 getter/setter;
  • 多环境用 spring.profiles.active 配 profile 文件,别把配置写死在代码里。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. Spring 入门课程
  2. 想看另一种讲法,参考 Spring 读取配置笔记 的实战思路;
  3. 写完想立刻试,在线运行 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.ymlapplication-test.ymlapplication-prod.yml 等 profile 专属文件,Spring 会按激活的环境自动合并,键重复时专属文件优先级更高。

0 人点赞