Spring Boot:多配置属性的整体获取与操作

Spring Boot:多配置属性的综合获取与操作

前一篇文章提及,使用@Value注解一次只能获取一个配置属性,若要一次性获取多个属性,或者获取具有结构关联的一组属性,可借助@ConfigurationProperties注解来处理。

@ConfigurationProperties注解的两种运用方式

  1. 修饰属性处理类:当被@ConfigurationProperties注解修饰的类成为容器中的Bean时,该注解所指定的属性会注入到该Bean的属性里。所以,被@ConfigurationProperties修饰的类被称作“属性处理类”。
  2. 修饰@Bean注解修饰的方法:使用@Bean修饰的方法会配置一个容器中的Bean,而@ConfigurationProperties指定的属性会注入到该Bean的属性中。

运用属性处理类获取配置属性

直接看代码示例:

\resources\application.properties文件内容如下:

org.crazyit.enabled=true
org.crazyit.name=Crazy Java
org.crazyit.remoteAddress=192.168.1.188
org.crazyit.item.brand=Tesla
org.crazyit.item.comments=Good, Excellent

(在使用IDEA开发属性处理类时,添加spring-boot-configuration-processor依赖,IDEA会提供“代码补全”功能。

<!-- 添加该依赖后IDEA可以提供自动补全功能 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
   <optional>true</optional>
</dependency>

SpringBoot不会自动启用@ConfigurationProperties注解,启用该注解有以下几种方式:
1. 给被@ConfigurationProperties注解修饰的类添加@Component注解;
2. 将被@ConfigurationProperties注解修饰的类显式配置成容器中的bean;
3. 使用@EnableConfigurationProperties注解,该注解显式指定一个或多个属性处理类,SpringBoot会启用这些属性处理类上的@ConfigurationProperties注解;
4. 使用@ConfigurationPropertiesScan注解,该注解指定一个或多个包及其子包下所有带有@ConfigurationProperties注解的类。

@ConfigurationProperties注解修饰的属性处理类代码示例

// 指定读取以org.crazyit.*开头的属性
@ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
@Component
public class CrazyitProperties {
    // 指定同名的实例变量和setter方法。SpringBoot会通过反射调用setter方法来完成属性值注入
    private boolean enabled;
    private String name;
    private InetAddress remoteAddress;
    private final Item item = new Item();
    // 省略getter\setter 
    public static class Item {
        private String brand;
        private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
        // 省略getter\setter 
    }
}

控制器类代码示例

@RestController
public class HelloController {
    private final CrazyitProperties crazyitProperties;
    // 依赖注入CrazyitProperties属性处理Bean
    @Autowired
    public HelloController(CrazyitProperties crazyitProperties) {
        this.crazyitProperties = crazyitProperties;
    }

    @GetMapping
    public CrazyitProperties hello() {
        return crazyitProperties;
    }
}

运行结果相关说明

属性处理类也支持通过构造器来注入属性值,但需要额外用@ConstructorBinding修饰用于构造注入的构造器。需要注意的是:使用构造器注入时,必须使用@EnableConfigurationProperties或者@ConfigurationPropertiesScan注解来启用@ConfigurationProperties注解。

属性处理类示例(构造器注入)

@ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
public class CrazyitProperties {
    private boolean enabled;
    private String name;
    private InetAddress remoteAddress;
    private final Item item;

    @ConstructorBinding
    public CrazyitProperties(boolean enabled, String name, InetAddress remoteAddress, Item item) {
        this.enabled = enabled;
        this.name = name;
        this.remoteAddress = remoteAddress;
        this.item = item;
    }
    // 省略getter\setter 

    public static class Item {
        private String brand;
        private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
        // 省略getter\setter 
    }
}

application.yml示例

org:
  crazyit:
    enabled: true
    name: 哈利波特
    remote-address: 192.168.1.188
    item:
      brand: Apple
      comments:
        - Good
        - Excellent

上述配置中,org.crazyit.remote-address与CrazyitProperties类中定义的remoteAddress属性不完全相同,SpringBoot能否成功注入呢?答案是肯定的,因为SpringBoot支持宽松绑定(Relaxed Binding),它不要求配置的属性名和属性处理类中的属性名完全一致。

宽松绑定的四种情况(以下四种情况均可完成注入)

  1. org.crazyit.remote-address
  2. org.crazyit. remoteAddress
  3. org.crazyit. remote_address
  4. ORG_CRAZYIT_REMOTEADDRESS

.properties和 .yml文件中配置List类型属性的区别

  • 在*.properties文件中配置List有两种方式:用英文逗号隔开多个值;使用标准方括号。
  • 在 *.yml文件中配置List有两种方式:用英文逗号隔开多个值;用短横线开头(如示例所示)。

因此,.properties和 .yml文件中,都可以用英文逗号隔开多个值来构建List。

App.java示例

@SpringBootApplication
// 指定扫描org.crazyit.app.config包及其子包下的@ConfigurationProperties注解修饰的类
@ConfigurationPropertiesScan("org.crazyit.app.config")
public class App {
    public static void main(String[] args) {
        // 创建Spring容器、运行Spring Boot应用
        SpringApplication.run(App.class, args);
    }
}

为容器中的bean注入配置属性

@ConfigurationProperties注解除了可以修饰类,还可以修饰@Bean注解修饰的方法。

Book类代码示例

public class Book {
    private String title;
    private double price;
    private String author;
    private List<String> keywords;
    // 省略getter、setter
}

配置类代码示例

@Configuration
public class MyConfig {
    @Bean
    // @ConfigurationProperties注解会驱动Spring自动调用该Bean的setter方法
    @ConfigurationProperties("fkjava.book")
    public Book book() {
        return new Book();
    }
}

application.yml示例

fkjava:
  book:
    title: "美语音标"
    price: 128
    author: "赖世雄"
    keywords:
      - 英语
      - 音标

控制器类代码示例

@RestController
public class HelloController {
    private final Book book;
    // 通过构造器注入Book对象
    @Autowired
    public HelloController(Book book) {
        this.book = book;
    }
    @GetMapping
    public Book hello() {
        return book;
    }
}

@Value和 @ConfigurationProperties的对比

特征 @ConfigurationProperties @Value
注入的属性个数 批量 单个
宽松绑定 支持 部分支持
SpEL 不支持 支持
元数据支持 支持 不支持

属性转换

SpringBoot内置了常用的类型转换机制,如果转换失败,应用启动会失败并抛出异常。若要忽略转换失败的属性,可将@ConfigurationProperties注解的ignoreInvaildFields属性设为true(默认是false)。

SpringBoot可自动转换如下类型:Duration、Period、DataSize,并支持为属性值指定单位。
- Duration类型可指定的单位:ns纳秒、μs微妙、ms毫秒、s秒、m分钟、h小时、d天。
- Period类型可指定的单位:y年、m月、w星期、d天。(例如配置1y3d,表示1年3天)。
- DataSize类型可指定的单位:B字节、KB千字节、MB兆字节、GB吉字节、TB太字节

代码示例:属性处理类

// 指定读取以org.crazyit.*开头的属性
@ConfigurationProperties(prefix = "org.crazyit")
@Component
public class CrazyitProperties {
    private Duration timeout;
    //   @DurationUnit注解指定默认的时间单位
    @DurationUnit(ChronoUnit.SECONDS)
    private Duration lastTime;
    private Period runPeriod;
    // @DataSizeUnit注解指定默认的数据单位
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize maxSize;
    // 省略getter、setter
}

application.properties示例

org.crazyit.timeout=30000
# 默认单位是秒
org.crazyit.last-time=45
org.crazyit.run-period=2m5d
org.crazyit.max-size=2

控制器类代码示例

@RestController
public class HelloController {
    private final CrazyitProperties crazyitProperties;
    // 依赖注入CrazyitProperties属性处理Bean
    @Autowired
    public HelloController(CrazyitProperties crazyitProperties) {
        this.crazyitProperties = crazyitProperties;
    }

    @GetMapping
    public Map<String, Object> hello() {
        System.out.println(crazyitProperties.getMaxSize().toKilobytes());
        return Map.of("props", crazyitProperties,
              "maxSize", crazyitProperties.getMaxSize().toKilobytes());
    }
}

校验@ConfigurationProperties

只要为属性处理类添加@Validated注解,并使用JSR303的校验注解修饰需要校验的示例变量,SpringBoot会自动校验配置文件中的属性值。如果某个属性校验不通过,应用会启动失败,并用FailureAnalyzer显式校验错误。

如果属性处理类包含复合类型的属性,且要对复合类型的子属性进行校验,则应为该复合类型的属性添加@Valid注解。

@ConfigurationProperties校验基于JSR303,因此必须添加JSR303依赖,spring-boot-starter-validation依赖库包含了JSR303的依赖,因此要在pom文件中添加以下依赖:

<!-- 添加Spring Boot Validation依赖库 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

属性处理类示例

// 指定读取以org.crazyit.*开头的属性
@ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
@Component
@Validated
public class CrazyitProperties {
   @NotEmpty
   private String name;
   @Range(max = 150, min=90, message = "价格必须位于90~150之间")
   private double price;
   @Pattern(regexp = "[1][3-8][0-9]{9}", message = "必须输入有效的手机号")
   private String mobile;
   @Valid
   private final Item item = new Item();
   // 省略getter setter 

   public static class Item {
      @Length(min=5, max=10, message = "品牌名长度必须在5到10个字符")
      private String brand;
      @Size(min = 1, message = "comments至少包含一个元素")
      private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
      // 省略getter、setter
   }
}

application.properties示例

org.crazyit.name=Crazy Java
org.crazyit.price=89
org.crazyit.mobile=13334444
org.crazyit.item.brand=Apple
org.crazyit.item.comments=Great, Excellent

对于使用@ConfigurationProperties修饰@Bean方法的情况,同样可对配置属性进行校验,主要为@Bean方法添加@Validated注解即可。

版权声明:程序员胖胖胖虎阿 发表于 2025年7月26日 上午11:59。
转载请注明:Spring Boot:多配置属性的整体获取与操作 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...