SpringBoot(31) - Properties(3) - 类型安全的配置属性

本文深入探讨SpringBoot中@ConfigurationProperties的使用,包括强类型配置、松绑定、属性转换及校验,对比@Value注解,适合希望深入了解SpringBoot配置机制的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:https://docs.spring.io/spring-boot/docs/1.5.17.RELEASE/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties

 

使用@Value("$ {property}")注解来注入配置属性有时会很麻烦,特别是如果正在使用多个属性或者数据本质上是分层的。 Spring Boot提供了一种使用属性的替代方法,允许强类型bean管理和验证应用程序的配置。

package com.example;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("foo")
public class FooProperties {
    private boolean enabled;
    private InetAddress remoteAddress;
    private final Security security = new Security();

    public boolean isEnabled() { ... }
    public void setEnabled(boolean enabled) { ... }
    public InetAddress getRemoteAddress() { ... }
    public void setRemoteAddress(InetAddress remoteAddress) { ... }

    public Security getSecurity() { ... }

    public static class Security {
        private String username;
        private String password;
        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        public String getUsername() { ... }
        public void setUsername(String username) { ... }
        public String getPassword() { ... }
        public void setPassword(String password) { ... }
        public List<String> getRoles() { ... }
        public void setRoles(List<String> roles) { ... }

    }
}

上面的POJO定义了以下属性:

  • foo.enabled:默认值false
  • foo.remote-address:从String强制转换
  • foo.security.username:使用嵌套的“security”,其名称由属性的名称决定。 特别是返回类型根本没有使用,可能是SecurityProperties
  • foo.security.password:
  • foo.security.roles:String集合

注:getter和setter通常是必需的,因为绑定是通过标准的Java Beans属性描述符,就像在Spring MVC中一样。 有些情况下可能会省略setter:

  • Map,只要它们被初始化,就需要一个getter,但setter不是必需的,因为它们可以被绑定器转换。
  • 可以通过索引(通常使用YAML)或使用单个逗号分隔值(属性)访问集合和数组。 在后一种情况下,必须使用setter。 建议始终为此类型添加setter。 如果初始化集合,请确保它是可变的(如上例所示)
  • 如果初始化嵌套的POJO属性(如上例中的“security”字段),则不需要setter。 如果希望绑定器使用其默认构造函数即时创建实例,则需要一个setter。

有些人使用Project Lombok自动添加getter和setter。 确保Lombok不为此类型生成任何特定构造函数,因为容器将自动使用它来实例化对象。

 

还需要列出要在@EnableConfigurationProperties注解中注册的属性类:

@Configuration
@EnableConfigurationProperties(FooProperties.class)
public class MyConfiguration {
}

注:

  • 当@ConfigurationProperties bean以这种方式注册时,bean将具有常规名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties注解中指定的环境键前缀,<fqn>是bean的完全限定名称。如果注解未提供任何前缀,则仅使用bean的完全限定名称。
  • 上例中的bean名称为foo-com.example.FooProperties。

即使上面的配置会为FooProperties创建一个常规bean,建议@ConfigurationProperties只处理环境,特别是不从上下文中注入其他bean。 话虽如此,@EnableConfigurationProperties注解也会自动应用于项目,以便从Environment配置任何使用@ConfigurationProperties注解的现有bean。 可以通过确保FooProperties已经是一个bean来链接上面的MyConfiguration:

@Component
@ConfigurationProperties(prefix="foo")
public class FooProperties {
    // ... see above
}

这种配置风格特别适用于SpringApplication外部YAML配置:

# application.yml
foo:
    remote-address: 192.168.1.1
    security:
        username: foo
        roles:
          - USER
          - ADMIN
# additional configuration as required

要使用@ConfigurationProperties bean,可以像使用任何其他bean一样注入它们。

@Service
public class MyService {
    private final FooProperties properties;

    @Autowired
    public MyService(FooProperties properties) {
        this.properties = properties;
    }
     //...
    @PostConstruct
    public void openConnection() {
        Server server = new Server(this.properties.getRemoteAddress());
        // ...
    }
}

注:使用@ConfigurationProperties还可以生成可供IDE使用的元数据文件,以便为自己的密钥提供自动完成功能,有关详细信息,请参阅附录B,配置元数据附录。

 

1. 第三方配置

除了使用@ConfigurationProperties来注释类之外,还可以在公共@Bean方法上使用它。 当想要将属性绑定到控件之外的第三方组件时,这可能特别有用。
要从Environment属性配置bean,请将@ConfigurationProperties添加到其bean注册:

@ConfigurationProperties(prefix = "bar")
@Bean
public BarComponent barComponent() {
    ...
}

使用bar前缀定义的任何属性都将以与上面的FooProperties示例类似的方式映射到该BarComponent bean。

 

2. 松绑定

Spring Boot使用一些宽松的规则将Environment属性绑定到@ConfigurationProperties bean,因此不需要在Environment属性名和bean属性名之间进行精确匹配。 这有用的常见示例包括虚线分隔(例如,context-path绑定到contextPath)和大写(例如PORT绑定到端口)环境属性。
例如,给定以下@ConfigurationProperties类:

@ConfigurationProperties(prefix="person")
public class OwnerProperties {
    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

可以使用以下属性名称:

  • user.headPic:标准驼峰语法
  • user.head-pic:虚线表示法,推荐在.properties和.yml文件中使用
  • user.head_pic:下划线表示法,在.properties和.yml文件中使用的可替代格式
  • USER_HEAD_PIC:大写格式,推荐系统变量使用

 

3. 属性转换

当Spring绑定到@ConfigurationProperties bean时,它将尝试将外部应用程序属性强制转换为正确的类型。 如果需要自定义类型转换,可以提供ConversionService bean(带有bean id conversionService)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义转换器(带有注释为@ConfigurationPropertiesBinding的bean定义)。

注:由于在应用程序生命周期中很早就请求了此bean,因此请确保限制ConversionService正在使用的依赖项。 通常,在创建时可能无法完全初始化所需的任何依赖项。 如果配置密钥强制不需要,可能希望重命名自定义ConversionService,并且只依赖于使用@ConfigurationPropertiesBinding限定的自定义转换器。

 

4. @ConfigurationProperties校验

只要用Spring的@Validated注解注释,Spring Boot就会尝试验证@ConfigurationProperties类。 可以直接在配置类上使用JSR-303 javax.validation约束注解。 只需确保符合条件的JSR-303实现在类路径中,然后向字段添加约束注解:

@ConfigurationProperties(prefix="foo")
@Validated
public class FooProperties {
    @NotNull
    private InetAddress remoteAddress;
    // ... getters and setters
}

为了验证嵌套属性的值,必须将关联字段注释为@Valid以触发其验证。 例如,在上面的FooProperties示例的基础上:

@ConfigurationProperties(prefix="connection")
@Validated
public class FooProperties {
    @NotNull
    private InetAddress remoteAddress;
    @Valid
    private final Security security = new Security();

    // ... getters and setters

    public static class Security {
        @NotEmpty
        public String username;
        // ... getters and setters
    }
}

还可以通过创建名为configurationPropertiesValidator的bean定义来添加自定义Spring Validator。 应该将@Bean方法声明为static。 配置属性验证器是在应用程序生命周期的早期创建的,并将@Bean方法声明为static,允许创建bean而无需实例化@Configuration类。 这避免了早期实例化可能导致的任何问题。 有一个属性验证示例,可以看到如何设置。

注:spring-boot-actuator模块包括一个暴露所有@ConfigurationProperties bean的端点。 只需将Web浏览器指向/configprops或使用等效的JMX端点即可。 

 

5. @ConfigurationProperties对比@Value

@Value是核心容器功能,它不提供与类型安全配置属性相同的功能。 下表总结了@ConfigurationProperties和@Value支持的功能:

特性@ConfigurationProperties@Value

松绑定

Yes

No

元数据的支持

Yes

No

SpEL表达式

No

Yes

如果为自己的组件定义一组配置键,建议将它们分组到使用@ConfigurationProperties注释的POJO中。 另请注意,由于@Value不支持宽松绑定,因此如果需要使用环境变量提供值,则它不是一个很好的选择。
最后,虽然可以在@Value中编写SpEL表达式,但不会从Application属性文件处理此类表达式。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值