【附录】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应用能够灵活地管理各种配置,为复杂的企业级应用提供了强大的配置管理支持。

相关推荐
AntBlack7 分钟前
每周学点 AI:ComfyUI + Modal 的一键部署脚本
人工智能·后端·aigc
5大大大大雄42 分钟前
docker容器日志处理
后端
zzywxc7871 小时前
AI在金融、医疗、教育、制造业等领域的落地案例(含代码、流程图、Prompt示例与图表)
人工智能·spring·机器学习·金融·数据挖掘·prompt·流程图
我是哪吒1 小时前
分布式微服务系统架构第170集:Kafka消费者并发-多节点消费-可扩展性
后端·面试·github
Badman2 小时前
分布式系统下的数据一致性-Redis分布式锁
redis·分布式·后端
Java水解2 小时前
盘点那些自带高级算法的SQL
后端
一只叫煤球的猫3 小时前
2025年基于Java21的的秒杀系统要怎么设计?来点干货
后端·面试·性能优化
方圆想当图灵3 小时前
《生产微服务》评估清单 CheckList
后端·微服务
服务端技术栈3 小时前
历时 1 个多月,我的第一个微信小程序「图片转 Excel」终于上线了!
前端·后端·微信小程序
计算机毕业设计指导3 小时前
基于Spring Boot的幼儿园管理系统
spring boot·后端·信息可视化