乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

@Value 注解

一句话定位:@Value 是 Spring Boot 的单个配置值注入器,通过 ${} 占位符从外部配置源读取属性值并注入到字段,支持 SpEL 表达式和默认值语法。


定义与作用

@Value 用于将单个配置属性注入到 Bean 的字段、方法参数或构造器参数中。它解决了"硬编码配置值"的问题,让代码中的端口号、超时时间、系统名称等可变参数从外部读取。

对比传统 SSM 的繁琐过程

维度传统 SSMSpring Boot + @Value
配置注入方式在 XML 中写 <property name="timeout" value="5000"/>直接在字段上标注 @Value("${timeout:5000}")
默认值支持需要在 XML 中额外配置 default-value支持 ${key:default} 语法
SpEL 支持需要配置 SpEL 解析器原生支持 #{} 表达式
代码侵入性无注解,但依赖 XML一个注解即可,无 XML

与 @ConfigurationProperties 的对比

维度@Value@ConfigurationProperties
绑定方式单个属性,逐个注入批量绑定,按前缀整体映射
类型支持基础类型 + SpEL 表达式支持复杂类型(List、Map、Duration)
松散绑定不支持(必须精确匹配键名)支持(max-pool-size → maxPoolSize)
校验支持无可配合 @Validated + JSR-303 校验
适用场景单个简单值(如端口、超时、开关)结构化配置组(如数据库连接参数)

适用位置与常用属性

适用位置

@Value 可以标注在:

  1. 字段级别:直接注入到字段(最常用)
  2. 方法参数级别:注入到 @Bean 方法的参数
  3. 构造器参数级别:配合 @Autowired 构造器注入
// 方式一:字段注入(最常用)
@Value("${server.port:8080}")
private int serverPort;

// 方式二:方法参数注入
@Bean
public DataSource dataSource(@Value("${spring.datasource.url}") String url) {
    // ...
}

// 方式三:构造器参数注入
@Component
public class SystemConfig {
    private final String systemName;
    
    public SystemConfig(@Value("${feixiang.student.system-name}") String systemName) {
        this.systemName = systemName;
    }
}

常用语法

语法说明示例
${key}读取配置值,必填@Value("${server.port}")
${key:default}读取配置值,提供默认值@Value("${server.port:8080}")
#{expression}SpEL 表达式@Value("#{2 * 60}")
${key} + #{expr}混合使用@Value("${app.name} #{T(java.time.LocalDate).now()}")

核心原理

@Value 注入流程

图解释:@Value 的处理分为三个阶段。第一阶段解析 ${} 占位符,从 Environment 中查找对应的属性值;第二阶段解析 #{} 中的 SpEL 表达式;第三阶段通过 ConversionService 将字符串结果转换为目标字段类型(如 "8080" → int),最后通过反射注入到字段中。

类型转换机制


完整示例

场景说明

飞翔科技学生成绩管理系统的 application.yml 中定义了多项配置。后端开发小崔需要在 SystemConfig 类中读取这些配置,用于系统启动时的自检日志打印。要求:端口号有默认值、系统名称必须读取、超时时间使用 SpEL 计算。

操作前:配置值硬编码

// 操作前:配置硬编码在代码中,每次改配置都要重新编译
package com.feixiang.student.config;

import org.springframework.stereotype.Component;

@Component
public class LegacySystemConfig {
    
    private int serverPort = 8080;           // 写死
    private String systemName = "飞翔科技学生成绩管理系统";  // 写死
    private int requestTimeout = 2 * 60 * 1000;  // 写死:2分钟
    private boolean debugMode = false;       // 写死
    
    // 如果产品经理要求把超时改为3分钟,必须改代码重新部署
    // 如果运维要求把端口改为80,必须改代码重新部署
}

痛点:所有配置值硬编码在 Java 源码中,任何修改都需要重新编译、打包、部署。

使用该注解的完整代码

步骤1:application.yml 配置

# application.yml
server:
  port: 8080

feixiang:
  student:
    system-name: "飞翔科技学生成绩管理系统"
    request-timeout-seconds: 120
    debug-mode: false
    allowed-origins: "http://localhost:3000"

步骤2:使用 @Value 注入配置

package com.feixiang.student.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SystemConfig {
    
    // 读取配置,带默认值:如果 application.yml 未配置,则使用 8080
    @Value("${server.port:8080}")
    private int serverPort;
    
    // 读取配置,无默认值:如果 application.yml 未配置,启动报错
    @Value("${feixiang.student.system-name}")
    private String systemName;
    
    // SpEL 表达式:读取秒数,转换为毫秒(用于兼容旧代码)
    @Value("#{${feixiang.student.request-timeout-seconds:120} * 1000}")
    private int requestTimeoutMs;
    
    // 布尔值注入
    @Value("${feixiang.student.debug-mode:false}")
    private boolean debugMode;
    
    // 字符串值注入
    @Value("${feixiang.student.allowed-origins:*}")
    private String allowedOrigins;
    
    public void printStartupInfo() {
        System.out.println("╔══════════════════════════════════════╗");
        System.out.println("║  " + systemName + "  ║");
        System.out.println("╠══════════════════════════════════════╣");
        System.out.println("║  服务端口: " + serverPort + "                       ║");
        System.out.println("║  请求超时: " + requestTimeoutMs + " ms");
        System.out.println("║  调试模式: " + debugMode + "                       ║");
        System.out.println("║  允许跨域: " + allowedOrigins + "              ║");
        System.out.println("╚══════════════════════════════════════╝");
    }
    
    // Getters
    public int getServerPort() { return serverPort; }
    public String getSystemName() { return systemName; }
    public int getRequestTimeoutMs() { return requestTimeoutMs; }
    public boolean isDebugMode() { return debugMode; }
    public String getAllowedOrigins() { return allowedOrigins; }
}

步骤3:启动验证

package com.feixiang.student;

import com.feixiang.student.config.SystemConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class StudentApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(StudentApplication.class, args);
        SystemConfig config = ctx.getBean(SystemConfig.class);
        config.printStartupInfo();
    }
}

操作后运行结果及分析

默认启动(无外部参数)输出:

╔══════════════════════════════════════╗
║  飞翔科技学生成绩管理系统  ║
╠══════════════════════════════════════╣
║  服务端口: 8080                       ║
║  请求超时: 120000 ms
║  调试模式: false                       ║
║  允许跨域: http://localhost:3000              ║
╚══════════════════════════════════════╝

通过命令行覆盖后启动:

java -jar student-system.jar \
  --server.port=9090 \
  --feixiang.student.debug-mode=true

输出:

╔══════════════════════════════════════╗
║  飞翔科技学生成绩管理系统  ║
╠══════════════════════════════════════╣
║  服务端口: 9090                       ║
║  请求超时: 120000 ms
║  调试模式: true                       ║
║  允许跨域: http://localhost:3000              ║
╚══════════════════════════════════════╝

分析:

  1. 默认值生效:server.port 被命令行参数覆盖为 9090,而 requestTimeoutMs 仍使用默认值计算出的 120000
  2. SpEL 计算:#{${feixiang.student.request-timeout-seconds:120} * 1000} 先读取配置值 120,再乘以 1000,得到 120000
  3. 布尔值注入:debugMode 从 false 被外部参数覆盖为 true
  4. 无默认值强制读取:systemName 没有默认值,如果 application.yml 中未配置,启动时会抛出 IllegalArgumentException

易错场景与面试考点

易错场景一:给复杂类型注入导致类型转换失败

小崔尝试用 @Value 注入一个列表:

// 错误示范:@Value 不支持直接注入 List
@Component
public class BadConfig {
    
    @Value("${feixiang.student.features}")
    private List<String> features;  // ← 启动时抛出 ConversionFailedException
}

对应的 YAML:

feixiang:
  student:
    features:
      - cache
      - log
      - monitor

后果:@Value 的 ConversionService 无法将 YAML 列表直接转换为 List<String>。它只能处理逗号分隔的字符串(如 "cache,log,monitor")。启动时抛出 ConversionFailedException。

正确做法:复杂类型(List、Map、嵌套对象)应使用 @ConfigurationProperties,而不是 @Value:

// 正确:使用 @ConfigurationProperties 绑定 List
@Component
@ConfigurationProperties(prefix = "feixiang.student")
public class FeixiangProperties {
    private List<String> features;  // 支持 List
    // getter + setter
}

易错场景二:默认值语法写错位置

小崔写了一个错误的默认值表达式:

// 错误示范:默认值语法错误
@Value("${feixiang.student.timeout:3000:5000}")
private int timeout;  // ← 解析失败

后果:Spring 的占位符解析器将 3000:5000 整体作为默认值,这不是一个合法的 int 值,启动时抛出 NumberFormatException。

正确做法:${key:default} 中只有一个冒号分隔键和默认值:

@Value("${feixiang.student.timeout:3000}")
private int timeout;

面试考点

Q:@Value 和 @ConfigurationProperties 的核心区别是什么?

@Value 适合单个简单属性的注入,支持 SpEL 表达式和默认值,但不支持松散绑定和复杂类型;@ConfigurationProperties 适合批量结构化配置,支持松散绑定、List/Map、嵌套对象,可配合 @Validated 校验。配置项超过3个或有嵌套结构时,优先用 @ConfigurationProperties。

Q:@Value 的默认值语法是什么?如果未配置会怎样?

语法是 ${key:default}。如果未配置且无默认值,启动时抛出 IllegalArgumentException: Could not resolve placeholder 'key'。

Q:@Value 支持哪些类型?

支持所有 Spring ConversionService 能转换的类型:基本类型(int、long、boolean、String)、包装类、Enum、Duration、DataSize、URI 等。但不支持直接注入 List、Map 或嵌套对象。

Q:@Value 的 SpEL 表达式能做什么?

#{} 内可以写 Spring Expression Language 表达式,例如:

  • 算术运算:#{2 * 60}
  • 读取其他 Bean 属性:#{@otherBean.property}
  • 调用静态方法:#{T(java.lang.Math).random()}
  • 三元表达式:#{${app.enabled} ? '开启' : '关闭'}
上一页
@ConfigurationProperties
下一页
配置属性优先级