找回密码
 立即注册
首页 业界区 业界 SpringBoot--如何整体读取多个配置属性及其相关操作 ...

SpringBoot--如何整体读取多个配置属性及其相关操作

袁勤 10 小时前
上篇文章讲到使用@Value注解每次只能读取一个配置属性,若要整体读取多个属性,或者读取具有某种结构关系的一组属性可使用@ConfigurationProperties注解来处理。
@ConfigurationProperties注解的两种用法:

  • 修饰属性处理类:当@ConfigurationProperties修饰的类被部署为容器中的Bean时,改注解指定的属性会被注入该Bean的属性。因此,@ConfigurationProperties修饰的类称为“属性处理类”。
  • 修饰@Bean注解修饰的方法:使用@Bean修饰的方法蒋会配置一个容器中的Bean,而@ConfigurationProperties指定的属性会被注入该Bean的属性。
看不懂没关系,下面会有代码示例。
在使用@ConfigurationProperties时可指定如下属性:

  • prefix(或value):指定要加载的属性的前缀
  • ignoreInvaildFields():指定是否忽略无效属性值。比如处理类定义了某个字段的类型是Integer,但在配置文件中的值是abc,这就是无效值。
  • ignoreUnknowFields():指定是否忽略未知的字段值。如果在配置文件中配置的属性比处理类需要的属性更多,那么多出来的属性就是未知属性。
使用属性处理类获取配置属性

直接来看代码示例:
\resources\application.properties文件内容:
  1. org.crazyit.enabled=true
  2. org.crazyit.name=Crazy Java
  3. org.crazyit.remoteAddress=192.168.1.188
  4. org.crazyit.item.brand=Tesla
  5. org.crazyit.item.comments=Good, Excellent
复制代码
(使用IDEA开发属性处理类时,添加spring-boot-configuration-processor依赖,IDEA会提供“代码补全”功能。
  1. <dependency>
  2.    <groupId>org.springframework.boot</groupId>
  3.    spring-boot-configuration-processor</artifactId>
  4.    <optional>true</optional>
  5. </dependency>
复制代码
)
SpringBoot不会自动启用@ConfigurationProperties注解,启用该注解有如下方式:

  • 为@ConfigurationProperties注解修饰的类加@Component注解
  • 为@ConfigurationProperties注解修饰的类显式配置成容器中的bean
  • 使用@EnableConfigurationProperties注解,该注解显式指定一个或多个属性处理类,SpringBoot会启用这些属性处理类上的@ConfigurationProperties注解
  • 使用@ConfigurationPropertiesScan注解,该注解指定一个或多个包及其子包下所有带@ConfigurationProperties注解的类
@ConfigurationProperties注解修饰的属性处理类代码:
  1. // 指定读取以org.crazyit.*开头的属性
  2. @ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
  3. @Component
  4. public class CrazyitProperties
  5. {
  6.     // 指定同名的实例变量和setter方法。SpringBoot会通过反射调用setter方法来完成属性值注入
  7.    private boolean enabled;
  8.    private String name;
  9.    private InetAddress remoteAddress;
  10.    private final Item item = new Item();
  11.          // 省略getter\setter
  12.    public static class Item
  13.    {
  14.       private String brand;
  15.       private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
  16.             // 省略getter\setter
  17.    }
  18. }
复制代码
控制器类代码示例:
  1. @RestController
  2. public class HelloController
  3. {
  4.    private final CrazyitProperties crazyitProperties;
  5.    // 依赖注入CrazyitProperties属性处理Bean
  6.    @Autowired
  7.    public HelloController(CrazyitProperties crazyitProperties)
  8.    {
  9.       this.crazyitProperties = crazyitProperties;
  10.    }
  11.    @GetMapping
  12.    public CrazyitProperties hello()
  13.    {
  14.       return crazyitProperties;
  15.    }
  16. }
复制代码
运行结果:
1.png

属性处理类也支持使用构造器完成属性值注入,但是要额外使用@ConstructorBinding修饰用于执行构造注入的构造器。注意:使用构造器注入,必须使用@EnableConfigurationProperties或者@ConfigurationPropertiesScan注解来启用@ConfigurationProperties注解。
属性处理类示例:
  1. @ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
  2. public class CrazyitProperties
  3. {
  4.    private boolean enabled;
  5.    private String name;
  6.    private InetAddress remoteAddress;
  7.    private final Item item;
  8.    @ConstructorBinding
  9.    public CrazyitProperties(boolean enabled, String name, InetAddress remoteAddress, Item item)
  10.    {
  11.       this.enabled = enabled;
  12.       this.name = name;
  13.       this.remoteAddress = remoteAddress;
  14.       this.item = item;
  15.    }
  16.        // 省略getter\setter
  17.    public static class Item
  18.    {
  19.       private String brand;
  20.       private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
  21.        // 省略getter\setter
  22.    }
  23. }
复制代码
application.yml
  1. org:
  2.   crazyit:
  3.     enabled: true
  4.     name: 哈利波特
  5.     remote-address: 192.168.1.188
  6.     item:
  7.       brand: Apple
  8.       comments:
  9.         - Good
  10.         - Excellent
复制代码
上面配置的属性为org.crazyit.remote-address与CrazyitProperties类中定义的remoteAddress属性不完全相同,SpringBoot能成功注入吗?
答案是肯定的,因为SpringBoot支持宽松绑定(Relaxed Binding)。它不要求配置的属性名和属性处理类中的属性名完全相同。
宽松绑定的四种情况(这4种情况都能完成注入):


  • org.crazyit.remote-address
  • org.crazyit. remoteAddress
  • org.crazyit. remote_address
  • ORG_CRAZYIT_REMOTEADDRESS
在*.properties和 *.yml文件中配置List类型的属性有较小的区别:

  • 在*.properties文件中配置List有2种方式:英文逗号隔开多个值;用标准方括号
  • 在 *.yml文件中配置List有2种方式:英文逗号隔开多个值;用短横线开头(例子所示)
因此,*.properties和 *.yml文件中,都可以用英文逗号隔开多个值来构建List。
App.java示例
  1. @SpringBootApplication
  2. // 指定扫描org.crazyit.app.config包及其子包下的@ConfigurationProperties注解修饰的类
  3. @ConfigurationPropertiesScan("org.crazyit.app.config")
  4. public class App
  5. {
  6.    public static void main(String[] args)
  7.    {
  8.       // 创建Spring容器、运行Spring Boot应用
  9.       SpringApplication.run(App.class, args);
  10.    }
  11. }
复制代码
为容器中的bean注入配置属性

@ConfigurationProperties注解除了可修饰类,还可修饰@Bean注解修饰的方法。
代码示例:Book类
  1. public class Book
  2. {
  3.    private String title;
  4.    private double price;
  5.    private String author;
  6.    private List<String> keywords;
  7.         // 省略getter、setter
  8. }
复制代码
代码示例:配置类
  1. @Configuration
  2. public class MyConfig
  3. {
  4.    @Bean
  5.    // @ConfigurationProperties注解会驱动Spring自动调用该Bean的setter方法
  6.    @ConfigurationProperties("fkjava.book")
  7.    public Book book()
  8.    {
  9.       return new Book();
  10.    }
  11. }
复制代码
代码示例:application.yml
  1. fkjava:
  2.   book:
  3.     title: "美语音标"
  4.     price: 128
  5.     author: "赖世雄"
  6.     keywords:
  7.       - 英语
  8.       - 音标
复制代码
代码示例:控制器类
  1. @RestController
  2. public class HelloController
  3. {
  4.    private final Book book;
  5.     // 通过构造器注入Book对象
  6.    @Autowired
  7.    public HelloController(Book book)
  8.    {
  9.       this.book = book;
  10.    }
  11.    @GetMapping
  12.    public Book hello()
  13.    {
  14.       return book;
  15.    }
  16. }
复制代码
运行结果:
2.png

@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太字节
代码示例:属性处理类
  1. // 指定读取以org.crazyit.*开头的属性
  2. @ConfigurationProperties(prefix = "org.crazyit")
  3. @Component
  4. public class CrazyitProperties
  5. {
  6.    private Duration timeout;
  7.     //   @DurationUnit注解指定默认的时间单位
  8.    @DurationUnit(ChronoUnit.SECONDS)
  9.    private Duration lastTime;
  10.    private Period runPeriod;
  11.     // @DataSizeUnit注解指定默认的数据单位
  12.    @DataSizeUnit(DataUnit.MEGABYTES)
  13.    private DataSize maxSize;
  14.         // 省略getter、setter
  15. }
复制代码
代码示例:application.properties
  1. org.crazyit.timeout=30000
  2. # 默认单位是秒
  3. org.crazyit.last-time=45
  4. org.crazyit.run-period=2m5d
  5. org.crazyit.max-size=2
复制代码
代码示例:控制器类
  1. @RestController
  2. public class HelloController
  3. {
  4.    private final CrazyitProperties crazyitProperties;
  5.    // 依赖注入CrazyitProperties属性处理Bean
  6.    @Autowired
  7.    public HelloController(CrazyitProperties crazyitProperties)
  8.    {
  9.       this.crazyitProperties = crazyitProperties;
  10.    }
  11.    @GetMapping
  12.    public Map<String, Object> hello()
  13.    {
  14.       System.out.println(crazyitProperties.getMaxSize().toKilobytes());
  15.       return Map.of("props", crazyitProperties,
  16.             "maxSize", crazyitProperties.getMaxSize().toKilobytes());
  17.    }
  18. }
复制代码
运行结果:
3.png

校验@ConfigurationProperties

只要为属性处理类添加@Validated注解,并使用JSR303的校验注解修饰需要校验的示例变量,SpringBoot会自动校验配置文件中的属性值。如果某个属性校验不通过,应用会启动失败,并用FailureAnalyzer显式校验错误。
如果属性处理类包含复合类型的属性,且要对复合类型的子属性进行校验,则应为该复合类型的属性添加@Valid注解。
@ConfigurationProperties校验基于JSR303,因此必须添加JSR303依赖,spring-boot-starter-validation依赖库包含了JSR303的依赖,因此要在pom文件中添加以下依赖
  1. <dependency>
  2.    <groupId>org.springframework.boot</groupId>
  3.    spring-boot-starter-validation</artifactId>
  4. </dependency>
复制代码
属性处理类示例:
  1. // 指定读取以org.crazyit.*开头的属性
  2. @ConfigurationProperties(prefix = "org.crazyit", ignoreUnknownFields=false)
  3. @Component
  4. @Validated
  5. public class CrazyitProperties
  6. {
  7.    @NotEmpty
  8.    private String name;
  9.    @Range(max = 150, min=90, message = "价格必须位于90~150之间")
  10.    private double price;
  11.    @Pattern(regexp = "[1][3-8][0-9]{9}", message = "必须输入有效的手机号")
  12.    private String mobile;
  13.    @Valid
  14.    private final Item item = new Item();
  15.         // 省略getter setter
  16.    public static class Item
  17.    {
  18.       @Length(min=5, max=10, message = "品牌名长度必须在5到10个字符")
  19.       private String brand;
  20.       @Size(min = 1, message = "comments至少包含一个元素")
  21.       private List<String> comments = new ArrayList<>(Collections.singleton("GREAT"));
  22.         // 省略getter、setter
  23.    }
  24. }
复制代码
代码示例:application.properties
  1. org.crazyit.name=Crazy Java
  2. org.crazyit.price=89
  3. org.crazyit.mobile=13334444
  4. org.crazyit.item.brand=Apple
  5. org.crazyit.item.comments=Great, Excellent
复制代码
运行结果:
4.png

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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册