在编写项目代码时,咱们要求更灵活的配置,更好的模块化整合。在 Spring Boot 项目中,为知足以上要求,咱们将大量的参数配置在 application.properties 或 application.yml 文件中,经过 @ConfigurationProperties
注解,咱们能够方便的获取这些参数值html
假设咱们正在搭建一个发送邮件的模块。在本地测试,咱们不想该模块真的发送邮件,因此咱们须要一个参数来「开关」 disable 这个功能。另外,咱们但愿为这些邮件配置一个默认的主题,这样,当咱们查看邮件收件箱,经过邮件主题能够快速判断出这是测试邮件java
在 application.properties 文件中建立这些参数:git
咱们可使用 @Value
注解或着使用 Spring Environment
bean 访问这些属性,是这种注入配置方式有时显得很笨重。咱们将使用更安全的方式(@ConfigurationProperties
)来获取这些属性github
@ConfigurationProperties
的基本用法很是简单:咱们为每一个要捕获的外部属性提供一个带有字段的类。请注意如下几点:面试
Spring 宽松绑定规则 (relaxed binding)
Spring使用一些宽松的绑定属性规则。所以,如下变体都将绑定到 hostName 属性上:spring
若是咱们将 MailModuleProperties 类型的 bean 注入到另外一个 bean 中,这个 bean 如今能够以类型安全的方式访问那些外部配置参数的值。shell
可是,咱们仍然须要让 Spring 知道咱们的 @ConfigurationProperties 类存在,以便将其加载到应用程序上下文中( 面试还不知道 BeanFactory 和 ApplicationContext 的区别?)编程
对于 Spring Boot,建立一个 MailModuleProperties 类型的 bean,咱们能够经过下面几种方式将其添加到应用上下文中数组
首先,咱们能够经过添加 @Component 注解让 Component Scan 扫描到安全
很显然,只有当类所在的包被 Spring @ComponentScan
注解扫描到才会生效,默认状况下,该注解会扫描在主应用类下的全部包结构
咱们也能够经过 Spring 的 Java Configuration 特性实现一样的效果:
只要 MailModuleConfiguration 类被 Spring Boot 应用扫描到,咱们就能够在应用上下文中访问 MailModuleProperties bean
咱们还可使用 @EnableConfigurationProperties
注解让咱们的类被 Spring Boot 所知道,在该注解中实际上是用了@Import(EnableConfigurationPropertiesImportSelector.class)
实现,你们能够看一下
激活一个 @ConfigurationProperties 类的最佳方式是什么?
全部上述方法都一样有效。然而,我建议模块化你的应用程序,并让每一个模块提供本身的
@ConfigurationProperties
类,只提供它须要的属性,就像咱们在上面的代码中对邮件模块所作的那样。这使得在不影响其余模块的状况下重构一个模块中的属性变得容易。所以,我不建议在应用程序类自己上使用
@EnableConfigurationProperties
,如许多其余教程中所示,是在特定于模块的 @Configuration 类上使用@EnableConfigurationProperties
,该类也能够利用包私有的可见性对应用程序的其他部分隐藏属性。
若是咱们在 application.properties 属性上定义的属性不能被正确的解析会发生什么?假如咱们为本来应该为布尔值的属性提供的值为 'foo':
默认状况下,Spring Boot 将会启动失败,并抛出异常:
Failed to bind properties under 'myapp.mail.enabled' to java.lang.Boolean: Property: myapp.mail.enabled Value: foo Origin: class path resource [application.properties]:1:20 Reason: failed to convert java.lang.String to java.lang.Boolean
当咱们为属性配置错误的值时,而又不但愿 Spring Boot 应用启动失败,咱们能够设置 ignoreInvalidFields
属性为 true (默认为 false)
这样,Spring Boot 将会设置 enabled 字段为咱们在 Java 代码里设定好的默认值。若是咱们没有设置默认值,enabled 将为 null,由于这里定义的是 boolean 的包装类 Boolean
和上面的状况有些相反,若是咱们在 application.properties 文件提供了 MailModuleProperties 类不知道的属性会发生什么?
默认状况下,Spring Boot 会忽略那些不能绑定到 @ConfigurationProperties
类字段的属性
然而,当配置文件中有一个属性实际上没有绑定到 @ConfigurationProperties
类时,咱们可能但愿启动失败。也许咱们之前使用过这个配置属性,可是它已经被删除了,这种状况咱们但愿被触发告知手动从 application.properties 删除这个属性
为了实现上述状况,咱们仅须要将 ignoreUnknownFields
属性设置为 false (默认是 true)
如今,应用启动时,控制台会反馈咱们异常信息
Binding to target [Bindable@cf65451 type = com.example.configurationproperties.properties.MailModuleProperties, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(value=myapp.mail, prefix=myapp.mail, ignoreInvalidFields=false, ignoreUnknownFields=false)]] failed: Property: myapp.mail.unknown-property Value: foo Origin: class path resource [application.properties]:3:29 Reason: The elements [myapp.mail.unknown-property] were left unbound.
弃用警告⚠️(Deprecation Warning)
ignoreUnknownFields
在将来 Spring Boot 的版本中会被标记为 deprecated,由于咱们可能有两个带有@ConfigurationProperties
的类,同时绑定到了同一个命名空间 (namespace) 上,其中一个类可能知道某个属性,另外一个类殊不知道某个属性,这样就会致使启动失败
若是咱们但愿配置参数在传入到应用中时有效的,咱们能够经过在字段上添加 bean validation
注解,同时在类上添加 @Validated
注解
若是咱们忘记在 application.properties 文件设置 enabled 属性,而且设置 defaultSubject 为空
应用启动时,咱们将会获得 BindValidationException
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myapp.mail' to com.example.configurationproperties.properties.MailModuleProperties failed: Property: myapp.mail.enabled Value: null Reason: must not be null Property: myapp.mail.defaultSubject Value: null Reason: must not be empty
固然这些默认的验证注解不能知足你的验证要求,咱们也能够自定义注解
若是你的验证逻辑很特殊,咱们能够实现一个方法,并用 @PostConstruct 标记,若是验证失败,方法抛出异常便可, 关于 @PostConstruct,能够查看 Spring Bean 的生命周期,我从哪里来?
多数状况,咱们传递给应用的参数是基本的字符串或数字。可是,有时咱们须要传递诸如 List 的数据类型
假如,咱们为邮件模块提供了一个 SMTP 服务的列表,咱们能够添加该属性到 MailModuleProperties 类中
咱们有两种方式让 Spring Boot 自动填充该 list 属性
在 application.properties 文件中以数组形式书写
YAML 自己支持 list 类型,因此能够在 application.yml 文件中添加:
set 集合也是这种方式的配置方式,再也不重复书写。另外YAML 是更好的阅读方式,井井有条,因此在实际应用中更推荐你们使用该种方式作数据配置
Spring Boot 内置支持从配置参数中解析 durations (持续时间),官网文档 给出了明确的说明
咱们既能够配置毫秒数数值,也可配置带有单位的文本:
官网上已明确说明,配置 duration 不写单位,默认按照毫秒来指定,咱们也可已经过 @DurationUnit 来指定单位:
经常使用单位以下:
ns
for nanoseconds (纳秒)us
for microseconds (微秒)ms
for milliseconds (毫秒)s
for seconds (秒)m
for minutes (分)h
for hours (时)d
for days (天)与 Duration 的用法一毛同样,默认单位是 byte (字节),能够经过 @DataSizeUnit 单位指定:
添加配置
可是,我测试的时候打印出来结果都是以 B (bytes) 来显示
常见单位以下:
B
for bytesKB
for kilobytesMB
for megabytesGB
for gigabytesTB
for terabytes有些状况,咱们想解析配置参数到咱们自定义的对象类型上,假设,咱们咱们设置最大包裹重量:
在 MailModuleProperties 中添加 Weight 属性
咱们能够模仿 DataSize 和 Duration 创造本身的 converter (转换器)
将其注册到 Spring Boot 上下文中
@ConfigurationPropertiesBinding
注解是让 Spring Boot 知道使用该转换器作数据绑定
咱们向项目中添加依赖:
从新 build 项目以后,configuration processor 会为咱们建立一个 JSON 文件:
这样,当咱们在 application.properties 和 application.yml 中写配置的时候会有自动提醒:
configuration processor 容许咱们标记某一个属性为 deprecated
咱们能够经过添加 @DeprecatedConfigurationProperty
注解到字段的 getter 方法上,来标示该字段为 deprecated,从新 build 项目,看看 JSON 文件发生了什么?
当咱们再编写配置文件时,已经给出了明确 deprecated 提示:
Spring Boot 的 @ConfigurationProperties
注解在绑定类型安全的 Java Bean 时是很是强大的,咱们能够配合其注解属性和 @DeprecatedConfigurationProperty
注解获取到更友好的编程方式,同时这样让咱们的配置更加模块化。
觉得 @ConfigurationProperties
注解知足咱们的所有须要了吗?其实否则,Spring 官网明确给出了该注解和 @Value
注解的对比:
若是使用 SpEL 表达式,咱们只能选择 @Value
注解
另外我以前在阅读 RabbitMQ 源码时,发现 RabbitProperties 类充分的利用了 @ConfigurationProperties
注解特性:
感受本身后知后觉,最近在思考,为何小时候要阅读和背诵古诗词,文言文等经典,由于这样写文章就能够轻松熟练的引用经典。技术也同样,各类框架的源码就是学生时代的古诗词和文言文,咱们要多多查看阅读,甚至背诵编程思想,这样就能够写出愈来愈优雅的代码
关于 @ConfigurationProperties
注解的使用,这里推荐 RabbitMQ Github 源码,只需看这一个类就能够,知道怎样充分利用这个注解.
Demo 代码获取,回复公众号「demo」,打开连接查看对应的子文件夹便可
以读侦探小说思惟轻松趣味学习 Java 技术栈相关知识,本着将复杂问题简单化,抽象问题具体化和图形化原则逐步分解技术问题,技术持续更新,请持续关注......
[center]
欢迎持续关注公众号:「日拱一兵」
- 前沿 Java 技术干货分享
- 高效工具汇总 | 回复「工具」
- 面试问题分析与解答
- 技术资料领取 | 回复「资料」
以读侦探小说思惟轻松趣味学习 Java 技术栈相关知识,本着将复杂问题简单化,抽象问题具体化和图形化原则逐步分解技术问题,技术持续更新,请持续关注......