原文
從Spring boot第一個版本以來,咱們可使用@ConfigurationProperties註解將屬性綁定到對象。也能夠指定屬性的各類不一樣格式。好比,person.first-name,person.firstName和PERSON_FIRSTNAME均可以使用。這個功能叫作「relaxed binding」。java
不幸的是,在spring boot 1.x,「relaxed binding」顯得太隨意了。從而使得很難來定義準確的綁定規則和指定使用的格式。在1.x的實現中,也很難對其進行修正。好比,在spring boot 1.x中,不能將屬性綁定到java.util.Set對象。spring
因此,在spring boot 2.0中,開始重構屬性綁定的功能。咱們添加了一些新的抽象類和一些全新的綁定API。在本篇文章中,咱們會介紹其中一些新的類和接口,並介紹添加他們的緣由,以及如何在本身的代碼中如何使用他們。app
若是你已經使用spring有一段時間,你應該對Environment比較熟悉了。這個接口繼承了PropertyResolver,讓你從一些PropertySource的實現解析屬性。spring-boot
Spring Framework提供了一些經常使用的PropertySource,如系統屬性,命令行屬性,屬性文件等。Spring Boot自動配置這些實現(好比加載application.properties)。this
比起直接使用已存在的PropertySource實現類,Spring Boot2.0引入了新的ConfigurationPropertySource接口。咱們引入這個新的接口來定義「relaxed binding」規則。命令行
該接口的主要API顯得很是簡單code
ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);
另外有個IterableConfigurationPropertySource變量實現了Iterable<ConfigurationPropertyNaame>,讓你能夠發現source包含的全部屬性名稱。orm
你能夠向下面這樣將Environment傳給ConfigurationPropertySources:對象
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
咱們同時提供了MapConfigurationPropertySource來幫你應付上面的場景。blog
若是規則明確,實現"relaxed binding"會簡單不少。一直使用一致的格式,而不須要去關係在source中的各類無規則的格式。
ConfigurationPropertyNames類來強制進行這些屬性命名規則,例如「use lowercase kebab-case names」,在代碼中使用person.first-name,在source中使用person.firstName或者PERSON_FIRSTNAME.
如指望的那樣,ConfigurationPropertySource返回ConfigurationProperty對象,裏面包含了屬性的取值,另外有個可選的Origin對象。
spring boot 2.0引入了新的接口Origin,可以指出屬性取值的準確位置。其中TextResourceOrigin是較爲經常使用的實現,會提供所加載的Resource,以及對應的行。
對於.properties和.yml文件,咱們寫了定製的souce加載器,使得追蹤成爲可能。一些spring boot的功能進行了重寫來追蹤信息。好比,屬性綁定的驗證異常如今會顯示:
*************************** APPLICATION FAILED TO START *************************** Description: Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'person' to scratch.PersonProperties failed: Property: person.name Value: Joe Origin: class path resource [application.properties]:1:13 Reason: length must be between 4 and 2147483647 Action: Update your application's configuration
org.springframework.boot.context.properties.bind.Binder類容許你使用多個ConfigurationPropertySource。準確的說,Binder使用Bindable並返回一個BindResult.
一個Bindable能夠是Java bean,或是一個複雜對象(如List<Person>).這裏是一些例子
Bindable.ofInstance(existingBean); Bindable.of(Integer.class); Bindable.listOf(Person.class); Bindable.of(resovableType);
Binable用來攜帶註解的信息,但不須要太過關注這個。
binder會返回BindResult,和java8 stream操做返回的Optional很類似,一個BinderResult表示bind的結果。
若是你嘗試獲取一個沒有綁定的對象,會拋出異常,另外有其餘的方法來設置沒有綁定時的缺省對象。
var bound = binder.bind("person.date-of-birth",Bindable.of(LocalDate.class)); //返回LocalDate,若是沒有則拋出異常 bound.get() //返回一個格式化時間,或是「No DOB" bound.map(dateFormatter::format).orElse("NO DOB"); //返回LocalDate或者拋出自定義異常 bound.orElseThrow(NoDateOfBirthException::new);
大部分ConfigurationPropertySource實現將值當字符串處理。當Binder須要將值轉化爲其餘類型時,會使用到Spring的ConversionService API。比較經常使用的是@NumberFormat和@DateFormat這兩個註解。
spring boot 2.0也引入了一些新的註解和轉換器。例如,你如今能夠將4s轉換成Duration。具體請參考org.springframework.boot.conver包。
在binding的過程當中,你可能會須要實現一些額外的邏輯。BindHandler接口提供了這樣的機會。每個BindHandler有onStart, onSuccess, onFailure和 onFinish方法供重載。
spring boot提供了一些handlers,用來支持已存在的@ConfigurationProperties binding。 好比 ValidationBindHandler能夠用於綁定對象的合法性校驗上。
如文章開頭所提到的,@ConfigurationProperties是在spring boot最初就加入的功能。因此大部分人會繼續使用該註解是不可避免的。
咱們計劃在spring boot2.1中繼續增強Binder的功能,而第一個想要支持的功能是不可變屬性綁定。另外相較getters和setters的綁定,使用基於構造器的綁定來代替:
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 }
咱們但願在spring boot2.0 中你能夠找到更好用的屬性綁定功能,並考慮升級你目前的方式。