领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多自从Spring Boot首次发布以来,就可以使用@ConfigurationProperties
注解将属性绑定到类。也可以以不同的形式指定属性名称。例如,person.first-name
、person.firstName
和PERSON_FIRSTNAME
可以互换使用。我们称此功能为“宽松绑定”。
不幸的是,在Spring Boot 1.x中,“宽松绑定”过于宽松。很难准确定义绑定规则以及何时可以使用特定格式。我们也开始收到一些很难用1.x实现修复的问题报告。例如,在Spring Boot 1.x中,无法将项目绑定到java.util.Set
。
因此,在Spring Boot 2.0中,我们重新设计了绑定的方式。我们添加了一些新的抽象,并开发了一个全新的绑定API。在这篇博文中,我们将介绍一些新的类和接口,并说明它们为什么被添加、它们的作用以及如何在您自己的代码中使用它们。
如果您已经使用Spring一段时间了,您可能熟悉Environment
抽象。此接口是一个PropertyResolver
,允许您从一些底层的PropertySource
实现中解析属性。
Spring Framework为常见事物提供PropertySource
实现,例如系统属性、命令行标志和属性文件。Spring Boot以对大多数应用程序有意义的方式自动配置这些实现(例如,加载application.properties
)。
Spring Boot 2.0 引入了一个新的ConfigurationPropertySource
接口,而不是直接使用现有的PropertySource
接口进行绑定。我们引入了一个新的接口,以便为我们提供一个逻辑位置来实现以前是绑定程序一部分的宽松绑定规则。
该接口的主要API非常简单。
ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);
还有一个IterableConfigurationPropertySource
变体实现了Iterable<ConfigurationPropertyName>
,以便您可以发现源包含的所有名称。
您可以使用以下代码将Spring Environment
适配到ConfigurationPropertySources
Iterable<ConfigurationPropertySource> sources =
ConfigurationPropertySources.get(environment);
如果您需要,我们还提供了一个简单的MapConfigurationPropertySource
实现。
事实证明,如果您将其限制在一个方向上,宽松属性名称的概念更容易实现。无论它们在底层源中如何表示,都应该始终使用规范形式在代码中访问属性。
ConfigurationPropertyName
类强制执行这些规范命名规则,这些规则基本上可以归结为“使用小写连接线命名法”。
例如,即使在底层源中使用了person.firstName
或PERSON_FIRSTNAME
,也应该在代码中将属性称为person.first-name
。
正如您所预期的那样,从ConfigurationPropertySource
返回的ConfigurationProperty
对象封装了实际的属性值,但它也可以包含一个可选的Origin
对象。
Origin
是Spring Boot 2.0中引入的一个新接口,它允许您精确定位加载值的精确位置。有很多Origin
实现,其中最有用的是TextResourceOrigin
。这提供了加载的Resource
的详细信息,以及值的行号和列号。
对于.properties
和.yaml
文件,我们编写了自定义加载器来跟踪文件加载时的源。一些现有的Spring Boot功能已被改造以利用源信息。例如,绑定程序验证异常现在显示无法绑定的值 *和* 源。以下是故障分析器如何显示错误:
*************************** 应用程序启动失败 ***************************
描述
绑定到目标org.springframework.boot.context.properties.bind.BindException: 无法将'person'下的属性绑定到scratch.PersonProperties失败
Property: person.name
Value: Joe
Origin: class path resource \[application.properties\]:1:13
Reason: length must be between 4 and 2147483647
操作
更新应用程序的配置
Binder
类(在org.springframework.boot.context.properties.bind
中)允许您获取一个或多个ConfigurationPropertySource
并从中绑定某些内容。更准确地说,Binder
获取一个Bindable
并返回一个BindResult
。
Bindable
可以是现有的Java bean、类类型或复杂的ResolvableType
(例如List<Person>
)。以下是一些示例
Bindable.ofInstance(existingBean);
Bindable.of(Integer.class);
Bindable.listOf(Person.class);
Bindable.of(resovableType);
Bindable
也用于携带注释信息,但您通常不需要担心这一点。
绑定程序不会直接返回绑定的对象,而是返回称为BindResult
的内容。与Java 8 Streams
返回Optional
的方式类似,BinderResult
表示可能已绑定也可能未绑定的内容。
如果您尝试获取未绑定对象的实际结果,则会引发异常。我们还提供了一些方法,允许您在未绑定任何内容时提供替代值或映射到不同的类型。
var bound = binder.bind("person.date-of-birth",
Bindable.of(LocalDate.class));
// Return LocalDate or throws if not bound
bound.get();
// Return a formatted date or "No DOB"
bound.map(dateFormatter::format).orElse("No DOB");
// Return LocalDate or throws a custom exception
bound.orElseThrow(NoDateOfBirthException::new);
大多数ConfigurationPropertySource
实现将其底层值公开为字符串。当Binder
需要将源值转换为不同的类型时,它会委托给Spring的ConversionService
API。如果您需要调整值转换的方式,您可以自由使用格式化程序注释,例如@NumberFormat
或@DateFormat
。
Spring Boot 2.0还引入了一些新的注释和转换器,这些注释和转换器对于绑定特别有用。例如,您现在可以将4s
等值转换为Duration
。有关详细信息,请查看org.springframework.boot.convert
包。
有时,您可能需要在绑定时实现其他逻辑,而BindHandler
接口提供了一种很好的方法来实现这一点。每个BindHandler
都有onStart
、onSuccess
、onFailure
和onFinish
方法,可以实现这些方法来覆盖行为。
Spring Boot 提供了许多处理器,主要用于支持现有的@ConfigurationProperties
绑定。例如,ValidationBindHandler
可用于对绑定对象应用Validator
验证。
正如本文开头所述,@ConfigurationProperties
自 Spring Boot 之初就已成为其一项功能。@ConfigurationProperties
很可能仍然是大多数人执行绑定时所使用的方式。
尽管我们重写了整个绑定过程,但大多数人似乎在升级 Spring Boot 1.5 应用程序时并没有遇到太多问题。只要您遵循迁移指南中的建议,您应该会发现一切都能正常运行。如果您在升级应用程序时遇到问题,请在GitHub 问题跟踪器 上报告这些问题,并提供一个可以重现问题的简短示例。
我们计划在 Spring Boot 2.1 中继续开发Binder
,我们想要支持的第一个功能是不可变的配置属性。如果当前需要 getter 和 setter 的配置属性可以使用基于构造函数的绑定,那就太好了。
public class Person {
private final String firstName;
private final String lastName;
private final LocalDateTime dateOfBirth;
public Person(String firstName, String lastName,
LocalDateTime dateOfBirth) {
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
}
// getters
}
我们认为构造函数绑定也适用于Kotlin 数据类。如果您有兴趣跟踪此功能的进度,请订阅问题 #8762。
我们希望您发现 Spring Boot 2.0 中的新绑定功能很有用,并考虑升级您现有的 Spring Boot 应用程序。
如果您想讨论绑定方面的一般性问题,或者您有具体的增强建议或问题,请加入我们在 Gitter 上的讨论。