35

【Springboot】注解@ConfigurationProperties让配置整齐而简单

 4 years ago
source link: https://segmentfault.com/a/1190000021556406
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 简介

前面我们用一篇文章《 【Spring】只想用一篇文章记录@Value的使用,不想再找其它了(附思维导图)

详细讲解了在Spring中如何使用 @Value 来实现我们对配置的需求,它功能强大、使用方便。但它也是有它的局限性的,比如对于邮件服务,我们配置有:

mail.hostname=smtp.qq.com
[email protected]
mail.password=123456
[email protected]
[email protected]

使用 @Value ,我们需要5个注解及5个独立的变量:

@Value("${mail.hostname}")
private String hostname;
@Value("${mail.username}")
private String username;
@Value("${mail.password}")
private String password;
@Value("${mail.to}")
private List<String> to;
@Value("${mail.cc}")
private List<String> cc;

这样非常不方便,容易出错,较难维护,不好传递。如果能把相同功能的配置组合起来,那配置就不会这么乱了。而 Springboot 为我们提供了注解 @ConfigurationProperties 完美解决了这个问题。现在我们来深入了解一下这个注解的强大之处。

2 启动注解的三种方式

启动 @ConfigurationProperties 有三种方式,分别是:

(1)属性类 @ConfigurationProperties +属性类 @Component

@Component
@ConfigurationProperties(prefix = "pkslow")
public class PkslowProperties {
    private String name;
    private List<String> emails;
    private Map<String, Integer> price;
  //getter and setter
}

在属性配置类上加注解 @ConfigurationProperties 是三种方式都需要的,第一种方式通过 @Component 声明为一个可用的Bean。实际不一定是 @Component@Service 等也是可以的。

(2)属性类 @ConfigurationProperties +配置类 @Bean

在配置类中通过 @Bean 声明:

@Configuration
public class Config {
    @Bean
    public PkslowProperties pkslowProperties(){
        return new PkslowProperties();
    }
}

(3)属性类 @ConfigurationProperties +配置类 @EnableConfigurationProperties

我们可以在Springboot启动类中加上注解 @EnableConfigurationProperties 来声明:

@SpringBootApplication
@EnableConfigurationProperties(PkslowProperties.class)
public class ConfigurationPropertiesDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigurationPropertiesDemoApplication.class, args);
    }
}

3 两大优点

3.1 宽松的绑定规则

支持宽松的绑定规则,以下格式都可以识别为 accountType 属性:

pkslow.accountType=QQ
pkslow.accounttype=QQ
pkslow.account_type=QQ
pkslow.account-type=QQ
pkslow.ACCOUNT_TYPE=QQ

3.2 支持多种属性类型

支持多种属性类型,Java类如下:

@Component
@ConfigurationProperties(prefix = "pkslow")
@Data
public class PkslowProperties {
    private String name;
    private List<String> emails;
    private Map<String, Integer> price;
    private Account mainAccount;
    private List<Account> emailAccounts;
    private Map<String, Account> friendAccounts;
    private Duration activeTime;
    private DataSize appFileSize;
}

配置如下:

#普通类型
pkslow.name=Larry
#List
pkslow.emails[0][email protected]
pkslow.emails[1][email protected]
#Map
pkslow.price.shoe=200
pkslow.price.pen=10
pkslow.price.book=43
#Object
pkslow.mainAccount.username=larry
pkslow.mainAccount.password=123456
pkslow.mainAccount.accountType=Main
#List<Object>
pkslow.emailAccounts[0].username=larry
pkslow.emailAccounts[0].password=******
pkslow.emailAccounts[0].accounttype=QQ
pkslow.emailAccounts[1].username=larry
pkslow.emailAccounts[1].password=xxxxxx
pkslow.emailAccounts[1].account_type=Gmail
pkslow.emailAccounts[2].username=larry
pkslow.emailAccounts[2].password=xxxxxx
pkslow.emailAccounts[2].account-type=163
pkslow.emailAccounts[3].username=larry
pkslow.emailAccounts[3].password=xxxxxx
pkslow.emailAccounts[3].ACCOUNT_TYPE=Apple
#Map<String, Object>
pkslow.friendAccounts.JJ.username=JJ
pkslow.friendAccounts.JJ.password=******
pkslow.friendAccounts.JJ.accountType=QQ
pkslow.friendAccounts.Larry.username=Larry
pkslow.friendAccounts.Larry.password=******
pkslow.friendAccounts.Larry.accountType=QQ
#Duration
pkslow.activeTime=30d
#DataSize
pkslow.appFileSize=10KB

Duration 为持续时间属性,可支持的单位有:

  • ns:nanosecond,纳秒
  • us:microsecond,微秒
  • ms:millisecond,毫秒
  • s:second,秒
  • m :minute,分
  • h:hour,小时
  • d :day,天

不写默认为毫秒,也可以通过注解 @DurationUnit 来指定单位。

@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;

DataSize 类似,用来表示文件大小,支持的单位有: B/KB/MB/GB/TB 。默认单位为 B ,可以用 @DataSizeUnit 指定单位。

4 属性转换失败处理

4.1 无法转换的类型

有时配置错误,就会无法转换成正常的类型,例如属性为布尔类型,却定义为 pkslow.enabled=open ,那肯定是无法转换的。默认会启动失败,并抛出异常。

Description:
Failed to bind properties under 'pkslow.enabled' to boolean:
    Property: pkslow.enabled
    Value: open
    Origin: class path resource [application.properties]:46:16
    Reason: failed to convert java.lang.String to boolean

Action:
Update your application's configuration

但如果我们并不想影响Springboot的启动,可以通过设置 ignoreInvalidFields 属性为 true (默认为 false),就会忽略错误的属性。

@Component
@ConfigurationProperties(prefix = "pkslow", ignoreInvalidFields = true)
public class PkslowProperties {
}

设置之后,错误的属性就会取默认值,如 nullfalse

4.2 未知的属性

如果写错的不是配置的值,而是配置的项,会发生什么呢?

#Java类没有该属性myAppName
pkslow.myAppName=pkslow

结果是什么也不会发生。

因为在默认情况下,Springboot 会忽略那些不能识别的字段。如果你希望它在这种情况下启动失败,可以配置 ignoreUnknownFieldsfalse ,默认是为 true 的。这样你就必须要删除这个配置错误的属性了。

@Component
@ConfigurationProperties(prefix = "pkslow", ignoreUnknownFields = false)
public class PkslowProperties {
}

有两点需要注意:

(1)如果设置 ignoreInvalidFieldstrue ,则 ignoreUnknownFields 不起作用;

(2)带有 @ConfigurationProperties 的不同的类不要使用相同的前缀(命名空间),容易造成冲突,如某个属性一个可用,一个不可用。

5 自定义转换器

如前面讲解的 DurationDataSize ,都是比较特殊的属性。实际上我们还可以自定义属性,并自定义转换器来实现属性绑定。

配置如下:

pkslow.convertAccount=Larry:123456:QQ

对应的属性为:

private Account convertAccount;

其中 Account 类如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private String username;
    private String password;
    private String accountType;
}

通过实现 Converter 接口自定义转换器如下:

public class AccountConverter implements Converter<String, Account> {
    @Override
    public Account convert(String s) {
        String[] strings = s.split(":");
        return new Account(strings[0], strings[1], strings[2]);
    }
}

通过注解 @ConfigurationPropertiesBinding 声明启用该转换器:

@Configuration
public class Config {
    @Bean
    @ConfigurationPropertiesBinding
    public AccountConverter accountConverter() {
        return new AccountConverter();
    }
}

完成以上,就可以使用自定义的属性和配置了。

6 使用Spring Boot Configuration Processor

自定义的属性在IDE中是有告警的,无法被识别成合法的配置。通过引入Springboot Configuration Processor可以解决这个问题,并且IDE还能启动自动补全功能。

引入:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

6.1 完成自动补全

引入依赖后,重新build一下project就可以了。它会为我们创建一个Json格式的文件:

6ZvQVjU.jpg!web

6.2 标记配置属性为 Deprecated

把注解 @DeprecatedConfigurationProperty 放在getter方法,该属性还会被显示为Deprecated:

@Component
@ConfigurationProperties(prefix = "pkslow")
public class PkslowProperties {
    private String name;
    @DeprecatedConfigurationProperty
    public String getName() {
        return name;
    }
}

自动补全和Deprecated的效果如下:

MrYB7ni.png!web

7 总结

本文通过代码案例详细讲解了 @ConfigurationProperties 的使用,demo的代码可关注公众号后台回复” ConfigurationProperties “获取。

欢迎关注公众号< 南瓜慢说 >,将持续为你更新...

Ijiqiym.jpg!web

多读书,多分享;多写作,多整理。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK