属性绑定

Spring Boot 提供两套属性注入机制:@Value(单值注入)和 @ConfigurationProperties(类型安全的批量绑定)。

@Value vs @ConfigurationProperties

对比@Value@ConfigurationProperties
注入粒度单个属性一组相关属性
类型安全有限完整(嵌套对象、集合、Duration 等)
校验支持@Validated + JSR-303
IDE 补全有(需配置元数据处理器)
SpEL 支持支持不支持
默认值${key:default}字段初始值
适用场景少量、临时、需要 SpEL模块化配置类,推荐

@Value

@Service
public class AppService {
 
    @Value("${server.port}")
    private int port;
 
    // 冒号后为默认值(属性不存在时使用)
    @Value("${app.name:未命名应用}")
    private String appName;
 
    // 注入列表(逗号分隔)
    @Value("${app.cors.origins:http://localhost:3000}")
    private List<String> corsOrigins;
 
    // SpEL 表达式:读取系统属性
    @Value("#{systemProperties['user.home']}")
    private String userHome;
 
    // SpEL:计算表达式
    @Value("#{${app.timeout} * 1000}")
    private long timeoutMs;
 
    // SpEL:引用其他 Bean 的属性
    @Value("#{dataSource.maxPoolSize}")
    private int maxConnections;
}

@Value 在 Bean 构造期间注入,构造函数参数无法使用 @Value,应改用 @ConfigurationProperties + 构造函数绑定。


@ConfigurationProperties

基础用法

@ConfigurationProperties(prefix = "app.mail")
@Data   // Lombok,生成 getter/setter(绑定要求 setter 存在)
public class MailProperties {
 
    /** 邮件服务器地址 */
    private String host = "smtp.example.com";
 
    /** 端口,默认 25 */
    private int port = 25;
 
    /** 发件人地址 */
    private String from;
 
    /** 是否启用 SSL */
    private boolean ssl = false;
 
    /** 连接超时 */
    private Duration connectionTimeout = Duration.ofSeconds(10);
}
app:
  mail:
    host: smtp.gmail.com
    port: 587
    from: noreply@example.com
    ssl: true
    connection-timeout: 30s

注册方式(二选一)

// 方式一:启动类或配置类上扫描(推荐,批量注册)
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.config")
public class Application { }
 
// 方式二:显式注册单个类
@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig { }
 
// 方式三:类上加 @Component(不推荐,混淆了配置与 Bean 的职责)
@Component
@ConfigurationProperties(prefix = "app.mail")
public class MailProperties { }

Relaxed Binding(宽松绑定)

Spring Boot 自动将不同格式的 key 统一映射到驼峰命名的 Java 字段:

配置 key                    Java 字段
─────────────────────────────────────────
app.max-pool-size     →     maxPoolSize     (kebab-case,推荐写法)
APP_MAX_POOL_SIZE     →     maxPoolSize     (环境变量 UPPER_SNAKE_CASE)
app.maxPoolSize       →     maxPoolSize     (camelCase)
app.max_pool_size     →     maxPoolSize     (snake_case)

环境变量优先级高于配置文件,在 Docker / K8s 中常用环境变量覆盖配置文件中的值。详见 配置管理


嵌套对象

@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
 
    private String name;
    private Server server = new Server();
    private Database database = new Database();
 
    @Data
    public static class Server {
        private String host = "localhost";
        private int port = 8080;
        private Ssl ssl = new Ssl();
 
        @Data
        public static class Ssl {
            private boolean enabled = false;
            private String keyStore;
            private String keyStorePassword;
        }
    }
 
    @Data
    public static class Database {
        private String url;
        private String username;
        private int maxPoolSize = 20;
        private Duration queryTimeout = Duration.ofSeconds(30);
    }
}
app:
  name: my-service
  server:
    host: api.example.com
    port: 443
    ssl:
      enabled: true
      key-store: classpath:keystore.p12
      key-store-password: ${SSL_PASSWORD}
  database:
    url: jdbc:mysql://db:3306/mydb
    username: app
    max-pool-size: 50
    query-timeout: 10s

集合与 Map 绑定

@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
 
    // List<String>
    private List<String> allowedOrigins = new ArrayList<>();
 
    // List<对象>
    private List<DataSource> dataSources = new ArrayList<>();
 
    // Map<String, String>
    private Map<String, String> headers = new HashMap<>();
 
    // Map<String, 对象>
    private Map<String, RouteConfig> routes = new HashMap<>();
 
    @Data
    public static class DataSource {
        private String url;
        private String username;
    }
 
    @Data
    public static class RouteConfig {
        private String target;
        private int timeout;
    }
}
app:
  allowed-origins:
    - http://localhost:3000
    - https://app.example.com
 
  data-sources:
    - url: jdbc:mysql://primary:3306/db
      username: root
    - url: jdbc:mysql://replica:3306/db
      username: reader
 
  headers:
    X-App-Name: my-service
    X-Version: "1.0"
 
  routes:
    users:
      target: http://user-service
      timeout: 3000
    orders:
      target: http://order-service
      timeout: 5000

特殊类型绑定

Spring Boot 自动处理以下类型,无需自定义 Converter:

@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {
 
    // Duration:30s / 5m / 1h / 1d
    private Duration connectTimeout = Duration.ofSeconds(30);
    private Duration sessionTimeout = Duration.ofMinutes(30);
 
    // DataSize:100MB / 1GB / 512KB
    private DataSize maxUploadSize = DataSize.ofMegabytes(10);
    private DataSize bufferSize = DataSize.ofKilobytes(8);
 
    // Charset
    private Charset encoding = StandardCharsets.UTF_8;
 
    // InetAddress
    private InetAddress bindAddress;
 
    // Resource
    private Resource templateFile;
 
    // Enum(大小写不敏感)
    private LogLevel logLevel = LogLevel.INFO;
}
app:
  connect-timeout: 30s
  session-timeout: 30m
  max-upload-size: 50MB
  buffer-size: 16KB
  log-level: warn   # 等价于 WARN

构造函数绑定(不可变配置)

Spring Boot 2.2+ 支持通过构造函数绑定,配置类不需要 setter,天然不可变:

@ConfigurationProperties(prefix = "app.jwt")
// Spring Boot 3.x 直接在类上加,无需 @ConstructorBinding
public record JwtProperties(
    String secret,
    Duration accessTokenExpiry,
    Duration refreshTokenExpiry
) {
    // record 自带 compact constructor,可在此做校验
    public JwtProperties {
        Objects.requireNonNull(secret, "JWT secret 不能为空");
        if (secret.length() < 32) {
            throw new IllegalArgumentException("JWT secret 长度不能小于 32 位");
        }
    }
}
app:
  jwt:
    secret: ${JWT_SECRET}
    access-token-expiry: 2h
    refresh-token-expiry: 7d

配置校验(@Validated)

@ConfigurationProperties(prefix = "app.mail")
@Validated   // 启动时若校验失败,应用拒绝启动
@Data
public class MailProperties {
 
    @NotBlank(message = "邮件服务器地址不能为空")
    private String host;
 
    @Min(1) @Max(65535)
    private int port = 25;
 
    @Email(message = "发件人地址格式不正确")
    @NotBlank
    private String from;
 
    @NotNull
    private Duration timeout = Duration.ofSeconds(10);
 
    // 嵌套对象也可以校验
    @Valid
    @NotNull
    private Ssl ssl = new Ssl();
 
    @Data
    public static class Ssl {
        @AssertTrue(message = "开启 SSL 时必须配置证书路径")
        private boolean certConfigured;
    }
}

参数校验规则详见 参数校验


IDE 自动补全支持

添加注解处理器后,IDE 在 application.yml 中编辑配置时自动提示字段名、类型和 Javadoc 说明:

// build.gradle
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

编译后生成 META-INF/spring-configuration-metadata.json,也可手动补充描述:

// META-INF/additional-spring-configuration-metadata.json
{
  "properties": [
    {
      "name": "app.mail.host",
      "type": "java.lang.String",
      "description": "SMTP 服务器地址,如 smtp.gmail.com"
    },
    {
      "name": "app.mail.port",
      "type": "java.lang.Integer",
      "description": "SMTP 端口,TLS 通常为 587,SSL 为 465",
      "defaultValue": 25
    }
  ]
}

相关链接

  • 配置管理 — application.yml 格式、优先级、外部化配置、加密
  • 环境与Profile — Profile 切换与多环境属性文件
  • 类型转换 — 自定义 Converter,处理 @ConfigurationProperties 不支持的类型
  • 参数校验@Validated 校验注解与 JSR-303 规范
  • 条件注解 — 根据配置属性值条件装配 Bean(@ConditionalOnProperty
  • 自动配置 — Spring Boot Starter 中 @ConfigurationProperties 的使用模式