前面我们用一篇文章《 【Spring】只想用一篇文章记录@Value的使用,不想再找其它了(附思维导图) 》
详细讲解了在Spring中如何使用 @Value
来实现我们对配置的需求,它功能强大、使用方便。但它也是有它的局限性的,比如对于邮件服务,我们配置有:
mail.hostname=smtp.qq.com mail.username=larry@qq.com mail.password=123456 mail.to=to@163.com mail.cc=cc@gmail.com
使用 @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
完美解决了这个问题。现在我们来深入了解一下这个注解的强大之处。
启动 @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); } }
支持宽松的绑定规则,以下格式都可以识别为 accountType
属性:
pkslow.accountType=QQ pkslow.accounttype=QQ pkslow.account_type=QQ pkslow.account-type=QQ pkslow.ACCOUNT_TYPE=QQ
支持多种属性类型,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]=larry@qq.com pkslow.emails[1]=larry@gmail.com #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
为持续时间属性,可支持的单位有:
不写默认为毫秒,也可以通过注解 @DurationUnit
来指定单位。
@DurationUnit(ChronoUnit.DAYS) private Duration timeInDays;
DataSize
类似,用来表示文件大小,支持的单位有: B/KB/MB/GB/TB 。默认单位为 B
,可以用 @DataSizeUnit
指定单位。
有时配置错误,就会无法转换成正常的类型,例如属性为布尔类型,却定义为 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 { }
设置之后,错误的属性就会取默认值,如 null
或 false
。
如果写错的不是配置的值,而是配置的项,会发生什么呢?
#Java类没有该属性myAppName pkslow.myAppName=pkslow
结果是什么也不会发生。
因为在默认情况下,Springboot 会忽略那些不能识别的字段。如果你希望它在这种情况下启动失败,可以配置 ignoreUnknownFields
为 false
,默认是为 true
的。这样你就必须要删除这个配置错误的属性了。
@Component @ConfigurationProperties(prefix = "pkslow", ignoreUnknownFields = false) public class PkslowProperties { }
有两点需要注意:
(1)如果设置 ignoreInvalidFields
为 true
,则 ignoreUnknownFields
不起作用;
(2)带有 @ConfigurationProperties
的不同的类不要使用相同的前缀(命名空间),容易造成冲突,如某个属性一个可用,一个不可用。
如前面讲解的 Duration
和 DataSize
,都是比较特殊的属性。实际上我们还可以自定义属性,并自定义转换器来实现属性绑定。
配置如下:
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(); } }
完成以上,就可以使用自定义的属性和配置了。
自定义的属性在IDE中是有告警的,无法被识别成合法的配置。通过引入Springboot Configuration Processor可以解决这个问题,并且IDE还能启动自动补全功能。
引入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
引入依赖后,重新build一下project就可以了。它会为我们创建一个Json格式的文件:
把注解 @DeprecatedConfigurationProperty
放在getter方法,该属性还会被显示为Deprecated:
@Component @ConfigurationProperties(prefix = "pkslow") public class PkslowProperties { private String name; @DeprecatedConfigurationProperty public String getName() { return name; } }
自动补全和Deprecated的效果如下:
本文通过代码案例详细讲解了 @ConfigurationProperties
的使用,demo的代码可关注公众号后台回复” ConfigurationProperties “获取。
欢迎关注公众号< 南瓜慢说 >,将持续为你更新...