【附录】Spring 配置属性绑定 基础及应用

此文是 【Spring 容器详解】-> 【ApplicationContext 做了哪些企业化的增强?】的支节点。

Spring的配置属性绑定(Configuration Properties)是ApplicationContext提供的重要企业级功能,它允许将外部配置文件(如properties、YAML、环境变量、系统属性等)中的配置值绑定到Java对象上,实现类型安全的配置管理。

核心注解

1. @ConfigurationProperties

这是配置属性绑定的核心注解,用于将配置文件中的属性绑定到Java Bean上。

java 复制代码
@ConfigurationProperties(prefix = "app")
@Component
public class AppProperties {
    
    private String name;
    private String version;
    private Database database = new Database();
    private Email email = new Email();
    private List<String> features = new ArrayList<>();
    private Map<String, String> metadata = new HashMap<>();
    
    // getter和setter方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }
    
    public Database getDatabase() { return database; }
    public void setDatabase(Database database) { this.database = database; }
    
    public Email getEmail() { return email; }
    public void setEmail(Email email) { this.email = email; }
    
    public List<String> getFeatures() { return features; }
    public void setFeatures(List<String> features) { this.features = features; }
    
    public Map<String, String> getMetadata() { return metadata; }
    public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; }
    
    // 嵌套配置类
    public static class Database {
        private String url;
        private String username;
        private String password;
        private int maxConnections = 10;
        private boolean ssl = false;
        
        // getter和setter方法
        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
        
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        
        public int getMaxConnections() { return maxConnections; }
        public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; }
        
        public boolean isSsl() { return ssl; }
        public void setSsl(boolean ssl) { this.ssl = ssl; }
    }
    
    public static class Email {
        private String host;
        private int port = 587;
        private String username;
        private String password;
        private boolean enableTls = true;
        
        // getter和setter方法
        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 String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        
        public boolean isEnableTls() { return enableTls; }
        public void setEnableTls(boolean enableTls) { this.enableTls = enableTls; }
    }
}

2. @EnableConfigurationProperties

启用配置属性绑定功能,通常在配置类上使用。

java 复制代码
@Configuration
@EnableConfigurationProperties
public class AppConfig {
    
    @Bean
    public DataSource dataSource(AppProperties appProperties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(appProperties.getDatabase().getUrl());
        dataSource.setUsername(appProperties.getDatabase().getUsername());
        dataSource.setPassword(appProperties.getDatabase().getPassword());
        return dataSource;
    }
    
    @Bean
    public JavaMailSender mailSender(AppProperties appProperties) {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(appProperties.getEmail().getHost());
        mailSender.setPort(appProperties.getEmail().getPort());
        mailSender.setUsername(appProperties.getEmail().getUsername());
        mailSender.setPassword(appProperties.getEmail().getPassword());
        
        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", appProperties.getEmail().isEnableTls());
        
        return mailSender;
    }
}

配置文件格式

1. Properties格式

properties 复制代码
# application.properties
app.name=My Application
app.version=1.0.0

# 数据库配置
app.database.url=jdbc:mysql://localhost:3306/mydb
app.database.username=root
app.database.password=password
app.database.max-connections=20
app.database.ssl=true

# 邮件配置
app.email.host=smtp.gmail.com
app.email.port=587
app.email.username=myapp@gmail.com
app.email.password=mypassword
app.email.enable-tls=true

# 特性列表
app.features[0]=feature1
app.features[1]=feature2
app.features[2]=feature3

# 元数据
app.metadata.key1=value1
app.metadata.key2=value2
app.metadata.key3=value3

2. YAML格式

yaml 复制代码
# application.yml
app:
  name: My Application
  version: 1.0.0
  
  database:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password
    max-connections: 20
    ssl: true
  
  email:
    host: smtp.gmail.com
    port: 587
    username: myapp@gmail.com
    password: mypassword
    enable-tls: true
  
  features:
    - feature1
    - feature2
    - feature3
  
  metadata:
    key1: value1
    key2: value2
    key3: value3

3. 环境变量格式

bash 复制代码
# 环境变量设置
export APP_NAME="My Application"
export APP_VERSION="1.0.0"
export APP_DATABASE_URL="jdbc:mysql://localhost:3306/mydb"
export APP_DATABASE_USERNAME="root"
export APP_DATABASE_PASSWORD="password"
export APP_DATABASE_MAX_CONNECTIONS="20"
export APP_DATABASE_SSL="true"
export APP_EMAIL_HOST="smtp.gmail.com"
export APP_EMAIL_PORT="587"
export APP_EMAIL_USERNAME="myapp@gmail.com"
export APP_EMAIL_PASSWORD="mypassword"
export APP_EMAIL_ENABLE_TLS="true"

配置属性绑定机制

1. 属性名映射规则

Spring使用以下规则将配置文件中的属性名映射到Java属性:

java 复制代码
@ConfigurationProperties(prefix = "app")
public class PropertyMappingExample {
    
    // 配置文件: app.database-url -> Java属性: databaseUrl
    private String databaseUrl;
    
    // 配置文件: app.database_url -> Java属性: databaseUrl
    private String databaseUrl;
    
    // 配置文件: app.databaseUrl -> Java属性: databaseUrl
    private String databaseUrl;
    
    // 配置文件: app.database-url -> Java属性: databaseUrl
    private String databaseUrl;
    
    // 配置文件: app.database.url -> Java属性: database.url (嵌套对象)
    private Database database;
    
    // 配置文件: app.features[0] -> Java属性: features[0] (列表)
    private List<String> features;
    
    // 配置文件: app.metadata.key1 -> Java属性: metadata.key1 (Map)
    private Map<String, String> metadata;
    
    // getter和setter方法...
}

2. 类型转换

Spring自动进行类型转换,支持多种数据类型:

java 复制代码
@ConfigurationProperties(prefix = "app")
public class TypeConversionExample {
    
    // 字符串
    private String name;
    
    // 数字类型
    private int port;
    private long timeout;
    private double ratio;
    
    // 布尔类型
    private boolean enabled;
    
    // 日期类型
    private LocalDate startDate;
    private LocalDateTime lastModified;
    
    // 枚举类型
    private Environment environment;
    
    // 集合类型
    private List<String> tags;
    private Set<Integer> ports;
    private Map<String, Object> settings;
    
    // 数组
    private String[] aliases;
    private int[] numbers;
    
    // 自定义类型(需要自定义转换器)
    private Duration timeout;
    private DataSize maxSize;
    
    // getter和setter方法...
}

public enum Environment {
    DEV, TEST, PROD
}

3. 嵌套对象绑定

java 复制代码
@ConfigurationProperties(prefix = "app")
public class NestedObjectExample {
    
    private Database database;
    private Email email;
    private Cache cache;
    
    public static class Database {
        private String url;
        private String username;
        private String password;
        private ConnectionPool connectionPool;
        
        public static class ConnectionPool {
            private int initialSize = 5;
            private int maxSize = 20;
            private int minIdle = 5;
            private Duration maxWait = Duration.ofSeconds(30);
            
            // getter和setter方法...
        }
        
        // getter和setter方法...
    }
    
    public static class Email {
        private String host;
        private int port;
        private Authentication authentication;
        
        public static class Authentication {
            private String username;
            private String password;
            private String mechanism = "PLAIN";
            
            // getter和setter方法...
        }
        
        // getter和setter方法...
    }
    
    public static class Cache {
        private String type = "redis";
        private Duration ttl = Duration.ofMinutes(30);
        private Redis redis = new Redis();
        
        public static class Redis {
            private String host = "localhost";
            private int port = 6379;
            private String password;
            private int database = 0;
            
            // getter和setter方法...
        }
        
        // getter和setter方法...
    }
    
    // getter和setter方法...
}

配置属性验证

1. Bean Validation注解

java 复制代码
@ConfigurationProperties(prefix = "app")
@Validated
public class ValidatedAppProperties {
    
    @NotBlank(message = "应用名称不能为空")
    private String name;
    
    @Pattern(regexp = "\\d+\\.\\d+\\.\\d+", message = "版本号格式不正确")
    private String version;
    
    @Valid
    private Database database;
    
    @Valid
    private Email email;
    
    public static class Database {
        @NotBlank(message = "数据库URL不能为空")
        @Pattern(regexp = "jdbc:[a-zA-Z]+://.*", message = "数据库URL格式不正确")
        private String url;
        
        @NotBlank(message = "数据库用户名不能为空")
        private String username;
        
        @NotBlank(message = "数据库密码不能为空")
        private String password;
        
        @Min(value = 1, message = "最大连接数必须大于0")
        @Max(value = 100, message = "最大连接数不能超过100")
        private int maxConnections = 10;
        
        // getter和setter方法...
    }
    
    public static class Email {
        @NotBlank(message = "邮件服务器地址不能为空")
        private String host;
        
        @Min(value = 1, message = "端口号必须大于0")
        @Max(value = 65535, message = "端口号不能超过65535")
        private int port = 587;
        
        @Email(message = "邮箱格式不正确")
        private String username;
        
        @NotBlank(message = "邮件密码不能为空")
        private String password;
        
        // getter和setter方法...
    }
    
    // getter和setter方法...
}

2. 自定义验证器

java 复制代码
@ConfigurationProperties(prefix = "app")
@Validated
public class CustomValidatedProperties {
    
    @ValidPort
    private int port;
    
    @ValidUrl
    private String url;
    
    @ValidDatabaseConnection
    private Database database;
    
    // getter和setter方法...
}

// 自定义端口验证注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PortValidator.class)
public @interface ValidPort {
    String message() default "端口号无效";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 端口验证器实现
public class PortValidator implements ConstraintValidator<ValidPort, Integer> {
    
    @Override
    public boolean isValid(Integer port, ConstraintValidatorContext context) {
        if (port == null) {
            return false;
        }
        return port > 0 && port <= 65535;
    }
}

// 自定义URL验证注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UrlValidator.class)
public @interface ValidUrl {
    String message() default "URL格式无效";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// URL验证器实现
public class UrlValidator implements ConstraintValidator<ValidUrl, String> {
    
    @Override
    public boolean isValid(String url, ConstraintValidatorContext context) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        try {
            new URL(url);
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }
}

// 自定义数据库连接验证注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DatabaseConnectionValidator.class)
public @interface ValidDatabaseConnection {
    String message() default "数据库连接配置无效";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 数据库连接验证器实现
public class DatabaseConnectionValidator implements ConstraintValidator<ValidDatabaseConnection, Database> {
    
    @Override
    public boolean isValid(Database database, ConstraintValidatorContext context) {
        if (database == null) {
            return false;
        }
        
        // 验证数据库连接
        try (Connection connection = DriverManager.getConnection(
                database.getUrl(), 
                database.getUsername(), 
                database.getPassword())) {
            return true;
        } catch (SQLException e) {
            return false;
        }
    }
}

配置属性元数据

1. 元数据文件

json 复制代码
// META-INF/additional-spring-configuration-metadata.json
{
  "properties": [
    {
      "name": "app.name",
      "type": "java.lang.String",
      "description": "应用程序名称",
      "defaultValue": "My Application"
    },
    {
      "name": "app.version",
      "type": "java.lang.String",
      "description": "应用程序版本号",
      "pattern": "\\d+\\.\\d+\\.\\d+"
    },
    {
      "name": "app.database.url",
      "type": "java.lang.String",
      "description": "数据库连接URL",
      "pattern": "jdbc:[a-zA-Z]+://.*"
    },
    {
      "name": "app.database.max-connections",
      "type": "java.lang.Integer",
      "description": "数据库最大连接数",
      "minimum": 1,
      "maximum": 100,
      "defaultValue": 10
    },
    {
      "name": "app.email.enable-tls",
      "type": "java.lang.Boolean",
      "description": "是否启用TLS加密",
      "defaultValue": true
    }
  ],
  "hints": [
    {
      "name": "app.environment",
      "values": [
        {
          "value": "dev",
          "description": "开发环境"
        },
        {
          "value": "test",
          "description": "测试环境"
        },
        {
          "value": "prod",
          "description": "生产环境"
        }
      ]
    }
  ]
}

2. 元数据注解

java 复制代码
@ConfigurationProperties(prefix = "app")
public class MetadataExample {
    
    @ConfigurationProperty(description = "应用程序名称", defaultValue = "My Application")
    private String name;
    
    @ConfigurationProperty(description = "应用程序版本号")
    @Pattern(regexp = "\\d+\\.\\d+\\.\\d+")
    private String version;
    
    @ConfigurationProperty(description = "数据库配置")
    private Database database;
    
    public static class Database {
        @ConfigurationProperty(description = "数据库连接URL")
        @Pattern(regexp = "jdbc:[a-zA-Z]+://.*")
        private String url;
        
        @ConfigurationProperty(description = "数据库最大连接数", defaultValue = "10")
        @Min(1) @Max(100)
        private int maxConnections;
        
        // getter和setter方法...
    }
    
    // getter和setter方法...
}

配置属性绑定示例

1. 基本使用示例

java 复制代码
@SpringBootApplication
@EnableConfigurationProperties
public class ConfigurationPropertiesApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ConfigurationPropertiesApplication.class, args);
    }
}

@Component
public class ConfigurationPropertiesExample {
    
    @Autowired
    private AppProperties appProperties;
    
    @PostConstruct
    public void printConfiguration() {
        System.out.println("应用名称: " + appProperties.getName());
        System.out.println("应用版本: " + appProperties.getVersion());
        
        System.out.println("数据库配置:");
        System.out.println("  URL: " + appProperties.getDatabase().getUrl());
        System.out.println("  用户名: " + appProperties.getDatabase().getUsername());
        System.out.println("  最大连接数: " + appProperties.getDatabase().getMaxConnections());
        System.out.println("  SSL: " + appProperties.getDatabase().isSsl());
        
        System.out.println("邮件配置:");
        System.out.println("  服务器: " + appProperties.getEmail().getHost());
        System.out.println("  端口: " + appProperties.getEmail().getPort());
        System.out.println("  用户名: " + appProperties.getEmail().getUsername());
        System.out.println("  TLS: " + appProperties.getEmail().isEnableTls());
        
        System.out.println("特性列表:");
        for (String feature : appProperties.getFeatures()) {
            System.out.println("  - " + feature);
        }
        
        System.out.println("元数据:");
        for (Map.Entry<String, String> entry : appProperties.getMetadata().entrySet()) {
            System.out.println("  " + entry.getKey() + ": " + entry.getValue());
        }
    }
}

2. 条件化配置

java 复制代码
@Configuration
public class ConditionalConfiguration {
    
    @Bean
    @ConditionalOnProperty(name = "app.database.type", havingValue = "mysql")
    public DataSource mysqlDataSource(AppProperties appProperties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl(appProperties.getDatabase().getUrl());
        dataSource.setUsername(appProperties.getDatabase().getUsername());
        dataSource.setPassword(appProperties.getDatabase().getPassword());
        return dataSource;
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.database.type", havingValue = "h2")
    public DataSource h2DataSource(AppProperties appProperties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl(appProperties.getDatabase().getUrl());
        dataSource.setUsername(appProperties.getDatabase().getUsername());
        dataSource.setPassword(appProperties.getDatabase().getPassword());
        return dataSource;
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.email.enable-tls", havingValue = "true")
    public JavaMailSender tlsMailSender(AppProperties appProperties) {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(appProperties.getEmail().getHost());
        mailSender.setPort(appProperties.getEmail().getPort());
        mailSender.setUsername(appProperties.getEmail().getUsername());
        mailSender.setPassword(appProperties.getEmail().getPassword());
        
        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        
        return mailSender;
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.email.enable-tls", havingValue = "false", matchIfMissing = true)
    public JavaMailSender plainMailSender(AppProperties appProperties) {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(appProperties.getEmail().getHost());
        mailSender.setPort(appProperties.getEmail().getPort());
        mailSender.setUsername(appProperties.getEmail().getUsername());
        mailSender.setPassword(appProperties.getEmail().getPassword());
        
        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        
        return mailSender;
    }
}

3. 配置属性监听

java 复制代码
@Component
public class ConfigurationChangeListener {
    
    @EventListener
    public void handleConfigurationChange(ConfigurationChangeEvent event) {
        System.out.println("配置发生变化: " + event.getPropertyName());
        System.out.println("旧值: " + event.getOldValue());
        System.out.println("新值: " + event.getNewValue());
        
        // 处理配置变化
        if ("app.database.url".equals(event.getPropertyName())) {
            // 重新初始化数据库连接
            reinitializeDatabaseConnection();
        }
    }
    
    private void reinitializeDatabaseConnection() {
        System.out.println("重新初始化数据库连接...");
        // 实现重新初始化逻辑
    }
}

// 配置变化事件
public class ConfigurationChangeEvent {
    private final String propertyName;
    private final Object oldValue;
    private final Object newValue;
    
    public ConfigurationChangeEvent(String propertyName, Object oldValue, Object newValue) {
        this.propertyName = propertyName;
        this.oldValue = oldValue;
        this.newValue = newValue;
    }
    
    // getter方法...
}

总结

Spring的配置属性绑定功能提供了强大的配置管理能力:

  1. 类型安全 - 配置值自动转换为Java类型
  2. 嵌套支持 - 支持复杂的嵌套配置结构
  3. 验证支持 - 集成Bean Validation进行配置验证
  4. 元数据支持 - 提供配置属性的描述和约束信息

这些特性使得Spring应用能够灵活地管理各种配置,为复杂的企业级应用提供了强大的配置管理支持。

相关推荐
Moonbit1 小时前
MoonBit 作者寄语 2025 级清华深圳新生
前端·后端·程序员
前端的阶梯1 小时前
开发一个支持支付功能的微信小程序的注意事项,含泪送上
前端·后端·全栈
咕噜分发企业签名APP加固彭于晏1 小时前
腾讯元器的优点是什么
前端·后端
AAA修煤气灶刘哥2 小时前
Swagger 用着糟心?试试 Knife4j,后端开发狂喜
后端·面试
bobz9652 小时前
MCP on windows
后端
泡海椒2 小时前
jquickexcel 全功能指南:从数据导入到精美导出的完整流程
后端
iOS开发上架哦3 小时前
移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
后端
百度Geek说3 小时前
PaddleMIX推出扩散模型推理加速Fast-Diffusers:自研蒸馏加速方法FLUX-Lightning实现4步图像生成
后端
gopher_looklook3 小时前
Go并发实战:singleflight 源码解读与二次封装
数据结构·后端·go
用户833810251223 小时前
我为什么做PmMock:让接口设计不再头疼
前端·后端